diff --git a/CHANGELOG.md b/CHANGELOG.md
index e168d5e6..5aa621f5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,15 @@ CHANGELOG for FlatCAM Evo beta
=================================================
+28.04.2022
+
+- in Isolation Plugin made sure that the last displayed message is the warning, in the case of using tool validation and the tool is not validated
+- some more work in the Excellon Editor - Drill adding
+- some fixes in the image loader when clicking the an image that is in the list of previous loaded files
+- a small fin in the SVG parser
+- added a new Tcl command that is returning the name of the active object ('get_active') to accompany the 'set_active' command
+- a small fix for the 2d graphic mode by replacing the q5agg matplotlib backend with the qtagg backend which should work with the Qt6
+
26.04.2022
- activated the 'View Source' option in the project menu context menu for the Geometry objects
diff --git a/appEditors/AppExcEditor.py b/appEditors/AppExcEditor.py
index a21eb997..3b61f685 100644
--- a/appEditors/AppExcEditor.py
+++ b/appEditors/AppExcEditor.py
@@ -174,11 +174,11 @@ class SelectEditorExc(FCShapeTool):
return ""
- # pos[0] and pos[1] are the mouse click coordinates (x, y)
+ # clicked_pos[0] and clicked_pos[1] are the mouse click coordinates (x, y)
# for storage in self.draw_app.storage_dict:
# for obj_shape in self.draw_app.storage_dict[storage].get_objects():
# minx, miny, maxx, maxy = obj_shape.geo.bounds
- # if (minx <= pos[0] <= maxx) and (miny <= pos[1] <= maxy):
+ # if (minx <= clicked_pos[0] <= maxx) and (miny <= clicked_pos[1] <= maxy):
# over_shape_list.append(obj_shape)
#
# try:
@@ -277,12 +277,20 @@ class DrillAdd(FCShapeTool):
self.drill_tool = ExcGenEditorTool(self.app, self.draw_app, plugin_name=_("Drill"))
self.drill_tool.run()
+ self.drill_tool.length = self.draw_app.last_length
+ if not self.draw_app.snap_x:
+ self.draw_app.snap_x = 0.0
+ if not self.draw_app.snap_y:
+ self.draw_app.snap_y = 0.0
+ self.drill_tool.ui.x_entry.set_value(float(self.draw_app.snap_x))
+ self.drill_tool.ui.y_entry.set_value(float(self.draw_app.snap_y))
+
self.app.ui.notebook.setTabText(2, _("Drill"))
if self.app.ui.splitter.sizes()[0] == 0:
self.app.ui.splitter.setSizes([1, 1])
- self.points = deepcopy(self.draw_app.pos) if \
- self.draw_app.pos and self.draw_app.pos[0] and self.draw_app.pos[1] else (0.0, 0.0)
+ self.points = deepcopy(self.draw_app.clicked_pos) if \
+ self.draw_app.clicked_pos and self.draw_app.clicked_pos[0] and self.draw_app.clicked_pos[1] else (0.0, 0.0)
self.drill_point = None
def click(self, point):
@@ -320,7 +328,7 @@ class DrillAdd(FCShapeTool):
except Exception:
pass
- # add the point to drills if the diameter is a key in the dict, if not, create it add the drill location
+ # add the point to drills if the diameter is a key in the dict, if not, create it and add the drill location
# to the value, as a list of itself
if self.selected_dia in self.draw_app.points_edit:
self.draw_app.points_edit[self.selected_dia].append(self.drill_point)
@@ -331,6 +339,11 @@ class DrillAdd(FCShapeTool):
self.geometry = DrawToolShape(self.util_shape(self.drill_point))
self.draw_app.in_action = False
self.complete = True
+
+ self.draw_app.last_length = self.drill_tool.length
+ self.drill_tool.ui.x_entry.set_value(float(self.draw_app.snap_x))
+ self.drill_tool.ui.y_entry.set_value(float(self.draw_app.snap_y))
+
self.draw_app.app.inform.emit('[success] %s' % _("Done."))
try:
self.draw_app.app.jump_signal.disconnect()
@@ -339,10 +352,10 @@ class DrillAdd(FCShapeTool):
def draw_cursor_data(self, pos=None, delete=False):
if pos is None:
- pos = self.draw_app.snap_x, self.draw_app.snap_y
+ pos = 0, 0
if not self.points:
- self.points = (0, 0)
+ self.points = self.draw_app.snap_x, self.draw_app.snap_y
if delete:
if self.draw_app.app.use_3d_engine:
@@ -360,11 +373,10 @@ class DrillAdd(FCShapeTool):
x = pos[0]
y = pos[1]
try:
- length = abs(np.sqrt((pos[0] - self.points[0]) ** 2 + (pos[1] - self.points[1]) ** 2))
+ length = abs(np.sqrt((x - self.points[0]) ** 2 + (y - self.points[1]) ** 2))
except IndexError:
length = self.draw_app.app.dec_format(0.0, self.draw_app.app.decimals)
- except TypeError:
- print(self.points)
+
units = self.draw_app.app.app_units.lower()
x_dec = str(self.draw_app.app.dec_format(x, self.draw_app.app.decimals)) if x else '0.0'
@@ -402,7 +414,7 @@ class DrillAdd(FCShapeTool):
QtCore.Qt.Key.Key_Slash, QtCore.Qt.Key.Key_Asterisk]:
try:
# VisPy keys
- if self.drill_tool.length == 0:
+ if self.drill_tool.length == self.draw_app.last_length:
self.drill_tool.length = str(key.name)
else:
self.drill_tool.length = str(self.drill_tool.length) + str(key.name)
@@ -420,8 +432,8 @@ class DrillAdd(FCShapeTool):
self.drill_tool.length = 0.0
return _("Failed.")
- first_pt = self.points
- last_pt = self.draw_app.app.mouse
+ first_pt = self.drill_tool.ui.x_entry.get_value(), self.drill_tool.ui.y_entry.get_value()
+ last_pt = self.draw_app.snap_x, self.draw_app.snap_y
seg_length = math.sqrt((last_pt[0] - first_pt[0])**2 + (last_pt[1] - first_pt[1])**2)
if seg_length == 0.0:
@@ -433,7 +445,7 @@ class DrillAdd(FCShapeTool):
self.clean_up()
return '[ERROR_NOTCL] %s %s' % (_("Failed."), str(err).capitalize())
- if self.points != (new_x, new_y):
+ if first_pt != (new_x, new_y):
self.draw_app.app.on_jump_to(custom_location=(new_x, new_y), fit_center=False)
self.make()
self.drill_point = (new_x, new_y)
@@ -1012,7 +1024,7 @@ class SlotAdd(FCShapeTool):
# text
self.draw_app.app.plotcanvas.text_cursor.font_size = fsize
self.draw_app.app.plotcanvas.text_cursor.text = cursor_text
- self.draw_app.app.plotcanvas.text_cursor.pos = x, y
+ self.draw_app.app.plotcanvas.text_cursor.clicked_pos = x, y
self.draw_app.app.plotcanvas.text_cursor.anchors = 'left', 'top'
if self.draw_app.app.plotcanvas.text_cursor.parent is None:
@@ -2064,12 +2076,14 @@ class AppExcEditor(QtCore.QObject):
self.key = None # Currently pressed key
self.modifiers = None
- self.x = None # Current mouse cursor pos
+ self.x = None # Current mouse cursor clicked_pos
self.y = None
- # Current snapped mouse pos
+ # Current snapped mouse clicked_pos
self.snap_x = None
self.snap_y = None
- self.pos = None
+ self.clicked_pos = None
+
+ self.last_length = 0.0
self.complete = False
@@ -3500,53 +3514,53 @@ class AppExcEditor(QtCore.QObject):
# right_button = 3
if event.button == 1:
- self.pos = self.canvas.translate_coords(event_pos)
-
+ self.clicked_pos = self.canvas.translate_coords(event_pos)
if self.app.grid_status():
- self.pos = self.app.geo_editor.snap(self.pos[0], self.pos[1])
+ self.clicked_pos = self.app.geo_editor.snap(self.clicked_pos[0], self.clicked_pos[1])
else:
- self.pos = (self.pos[0], self.pos[1])
+ self.clicked_pos = (self.clicked_pos[0], self.clicked_pos[1])
- self.app.ui.rel_position_label.setText("Dx: %.4f Dy: "
- "%.4f " % (0, 0))
+ self.on_canvas_click_left_handler()
- # Selection with left mouse button
- if self.active_tool is not None and event.button == 1:
- # Dispatch event to active_tool
- # msg = self.active_tool.click(self.app.geo_editor.snap(event.xdata, event.ydata))
- self.active_tool.click(self.app.geo_editor.snap(self.pos[0], self.pos[1]))
+ def on_canvas_click_left_handler(self, custom_pos=None):
+ self.app.ui.rel_position_label.setText("Dx: %.4f Dy: "
+ "%.4f " % (0, 0))
- # If it is a shape generating tool
- if isinstance(self.active_tool, FCShapeTool) and self.active_tool.complete:
- if self.current_storage is not None:
- self.on_exc_shape_complete(self.current_storage)
- self.build_ui()
+ click_position = custom_pos if custom_pos is not None else self.clicked_pos
+ if self.active_tool is not None:
+ # Dispatch event to active_tool
+ self.active_tool.click(click_position)
- # MS: always return to the Select Tool if modifier key is not pressed
- # else return to the current tool
- key_modifier = QtWidgets.QApplication.keyboardModifiers()
- if self.app.options["global_mselect_key"] == 'Control':
- modifier_to_use = Qt.KeyboardModifier.ControlModifier
- else:
- modifier_to_use = Qt.KeyboardModifier.ShiftModifier
+ # If it is a shape generating tool
+ if isinstance(self.active_tool, FCShapeTool) and self.active_tool.complete:
+ if self.current_storage is not None:
+ self.on_exc_shape_complete(self.current_storage)
+ self.build_ui()
- # if modifier key is pressed then we add to the selected list the current shape but if it's already
- # in the selected list, we removed it. Therefore first click selects, second deselects.
- if key_modifier == modifier_to_use:
+ # MS: always return to the Select Tool if modifier key is not pressed
+ # else return to the current tool
+ if self.app.options["global_mselect_key"] == 'Control':
+ modifier_to_use = Qt.KeyboardModifier.ControlModifier
+ else:
+ modifier_to_use = Qt.KeyboardModifier.ShiftModifier
+
+ # if modifier key is pressed then we add to the selected list the current shape but if it's already
+ # in the selected list, we removed it. Therefore, first click selects, second deselects.
+ if QtWidgets.QApplication.keyboardModifiers() == modifier_to_use:
+ self.select_tool(self.active_tool.name)
+ else:
+ # return to Select tool but not for FCDrillAdd or SlotAdd
+ if isinstance(self.active_tool, DrillAdd) or isinstance(self.active_tool, SlotAdd):
self.select_tool(self.active_tool.name)
else:
- # return to Select tool but not for FCDrillAdd or SlotAdd
- if isinstance(self.active_tool, DrillAdd) or isinstance(self.active_tool, SlotAdd):
- self.select_tool(self.active_tool.name)
- else:
- self.select_tool("drill_select")
- return
+ self.select_tool("drill_select")
+ return
- if isinstance(self.active_tool, SelectEditorExc):
- # self.app.log.debug("Replotting after click.")
- self.replot()
- else:
- self.app.log.debug("No active tool to respond to click!")
+ if isinstance(self.active_tool, SelectEditorExc):
+ # self.app.log.debug("Replotting after click.")
+ self.replot()
+ else:
+ self.app.log.debug("No active tool to respond to click!")
def on_exc_click_release(self, event):
"""
@@ -3611,11 +3625,11 @@ class AppExcEditor(QtCore.QObject):
try:
if event.button == 1: # left click
if self.app.selection_type is not None:
- self.draw_selection_area_handler(self.pos, pos, self.app.selection_type)
+ self.draw_selection_area_handler(self.clicked_pos, pos, self.app.selection_type)
self.app.selection_type = None
elif isinstance(self.active_tool, SelectEditorExc):
- self.active_tool.click_release((self.pos[0], self.pos[1]))
+ self.active_tool.click_release((self.clicked_pos[0], self.clicked_pos[1]))
# if there are selected objects then plot them
if self.selected:
@@ -3687,10 +3701,10 @@ class AppExcEditor(QtCore.QObject):
self.snap_x = x
self.snap_y = y
- if self.pos is None:
- self.pos = (0, 0)
- self.app.dx = x - self.pos[0]
- self.app.dy = y - self.pos[1]
+ if self.clicked_pos is None:
+ self.clicked_pos = (0, 0)
+ self.app.dx = x - self.clicked_pos[0]
+ self.app.dy = y - self.clicked_pos[1]
# # update the position label in the infobar since the APP mouse event handlers are disconnected
# self.app.ui.position_label.setText(" X: %.4f "
@@ -3725,15 +3739,15 @@ class AppExcEditor(QtCore.QObject):
isinstance(self.active_tool, SlotAdd) or isinstance(self.active_tool, SlotArray):
self.app.selection_type = None
else:
- dx = pos[0] - self.pos[0]
+ dx = pos[0] - self.clicked_pos[0]
self.app.delete_selection_shape()
if dx < 0:
- self.app.draw_moving_selection_shape((self.pos[0], self.pos[1]), (x, y),
+ self.app.draw_moving_selection_shape((self.clicked_pos[0], self.clicked_pos[1]), (x, y),
color=self.app.options["global_alt_sel_line"],
face_color=self.app.options['global_alt_sel_fill'])
self.app.selection_type = False
else:
- self.app.draw_moving_selection_shape((self.pos[0], self.pos[1]), (x, y))
+ self.app.draw_moving_selection_shape((self.clicked_pos[0], self.clicked_pos[1]), (x, y))
self.app.selection_type = True
else:
self.app.selection_type = None
diff --git a/appEditors/AppGeoEditor.py b/appEditors/AppGeoEditor.py
index cc033165..d52f8e71 100644
--- a/appEditors/AppGeoEditor.py
+++ b/appEditors/AppGeoEditor.py
@@ -3203,7 +3203,7 @@ class AppGeoEditor(QtCore.QObject):
grid0.addWidget(self.is_simple_entry, 18, 1, 1, 2)
# Length
- len_lbl = FCLabel('%s' % _("Length"), bold=True)
+ len_lbl = FCLabel('%s' % _("Projection"), bold=True)
len_lbl.setToolTip(
_("The length of the geometry element.")
)
diff --git a/appEditors/exc_plugins/ExcCopyPlugin.py b/appEditors/exc_plugins/ExcCopyPlugin.py
index e7a36c37..597f011e 100644
--- a/appEditors/exc_plugins/ExcCopyPlugin.py
+++ b/appEditors/exc_plugins/ExcCopyPlugin.py
@@ -147,7 +147,7 @@ class ExcCopyEditorUI:
self.copy_tool_box.addLayout(grid0)
# Project distance
- self.project_line_lbl = FCLabel('%s:' % _("Length"))
+ self.project_line_lbl = FCLabel('%s:' % _("Projection"))
self.project_line_lbl.setToolTip(
_("Length of the current segment/move.")
)
diff --git a/appEditors/exc_plugins/ExcDrillArrayPlugin.py b/appEditors/exc_plugins/ExcDrillArrayPlugin.py
index d4eb9449..901266d2 100644
--- a/appEditors/exc_plugins/ExcDrillArrayPlugin.py
+++ b/appEditors/exc_plugins/ExcDrillArrayPlugin.py
@@ -147,7 +147,7 @@ class ExcDrillArrayEditorUI:
self.copy_tool_box.addLayout(grid0)
# Project distance
- self.project_line_lbl = FCLabel('%s:' % _("Length"))
+ self.project_line_lbl = FCLabel('%s:' % _("Projection"))
self.project_line_lbl.setToolTip(
_("Length of the current segment/move.")
)
diff --git a/appEditors/exc_plugins/ExcGenPlugin.py b/appEditors/exc_plugins/ExcGenPlugin.py
index 076b9763..d8b0f259 100644
--- a/appEditors/exc_plugins/ExcGenPlugin.py
+++ b/appEditors/exc_plugins/ExcGenPlugin.py
@@ -100,8 +100,8 @@ class ExcGenEditorUI:
def __init__(self, layout, path_class, plugin_name):
self.pluginName = plugin_name
- self.path_class = path_class
- self.decimals = self.path_class.app.decimals
+ self.ed_class = path_class
+ self.decimals = self.ed_class.app.decimals
self.layout = layout
# Title
@@ -119,38 +119,63 @@ class ExcGenEditorUI:
self.path_tool_frame = QtWidgets.QFrame()
self.path_tool_frame.setContentsMargins(0, 0, 0, 0)
self.layout.addWidget(self.path_tool_frame)
- self.path_tool_box = QtWidgets.QVBoxLayout()
- self.path_tool_box.setContentsMargins(0, 0, 0, 0)
- self.path_tool_frame.setLayout(self.path_tool_box)
+ self.editor_vbox = QtWidgets.QVBoxLayout()
+ self.editor_vbox.setContentsMargins(0, 0, 0, 0)
+ self.path_tool_frame.setLayout(self.editor_vbox)
- # Grid Layout
- grid_path = GLay(v_spacing=5, h_spacing=3)
- self.path_tool_box.addLayout(grid_path)
+ # Position
+ self.pos_lbl = FCLabel('%s' % _("Position"), bold=True, color='red')
+ self.editor_vbox.addWidget(self.pos_lbl)
+ # #############################################################################################################
+ # Position Frame
+ # #############################################################################################################
+ pos_frame = FCFrame()
+ self.editor_vbox.addWidget(pos_frame)
+
+ pos_grid = GLay(v_spacing=5, h_spacing=3)
+ pos_frame.setLayout(pos_grid)
+
+ # X Pos
+ self.x_lbl = FCLabel('%s:' % _("X"))
+ self.x_entry = FCDoubleSpinner()
+ self.x_entry.set_precision(self.decimals)
+ self.x_entry.set_range(-10000.0000, 10000.0000)
+ pos_grid.addWidget(self.x_lbl, 2, 0)
+ pos_grid.addWidget(self.x_entry, 2, 1)
+
+ # Y Pos
+ self.y_lbl = FCLabel('%s:' % _("Y"))
+ self.y_entry = FCDoubleSpinner()
+ self.y_entry.set_precision(self.decimals)
+ self.y_entry.set_range(-10000.0000, 10000.0000)
+ pos_grid.addWidget(self.y_lbl, 4, 0)
+ pos_grid.addWidget(self.y_entry, 4, 1)
+
+ # #############################################################################################################
+ # Projection Frame
+ # #############################################################################################################
+ pro_frame = FCFrame()
+ self.editor_vbox.addWidget(pro_frame)
+
+ pro_grid = GLay(v_spacing=5, h_spacing=3, c_stretch=[0, 1, 0])
+ pro_frame.setLayout(pro_grid)
# Project distance
- self.project_line_lbl = FCLabel('%s:' % _("Length"))
+ self.project_line_lbl = FCLabel('%s:' % _("Projection"))
self.project_line_lbl.setToolTip(
_("Length of the current segment/move.")
)
self.project_line_entry = NumericalEvalEntry(border_color='#0069A9')
- grid_path.addWidget(self.project_line_lbl, 0, 0)
- grid_path.addWidget(self.project_line_entry, 0, 1)
+ pro_grid.addWidget(self.project_line_lbl, 0, 0)
+ pro_grid.addWidget(self.project_line_entry, 0, 1)
- # self.buffer_corner_lbl = FCLabel('%s:' % _("Buffer corner"))
- # self.buffer_corner_lbl.setToolTip(
- # _("There are 3 types of corners:\n"
- # " - 'Round': the corner is rounded for exterior buffer.\n"
- # " - 'Square': the corner is met in a sharp angle for exterior buffer.\n"
- # " - 'Beveled': the corner is a line that directly connects the features meeting in the corner")
- # )
- # self.buffer_corner_cb = FCComboBox()
- # self.buffer_corner_cb.addItem(_("Round"))
- # self.buffer_corner_cb.addItem(_("Square"))
- # self.buffer_corner_cb.addItem(_("Beveled"))
- # grid_path.addWidget(self.buffer_corner_lbl, 2, 0)
- # grid_path.addWidget(self.buffer_corner_cb, 2, 1)
+ self.clear_btn = QtWidgets.QToolButton()
+ self.clear_btn.setIcon(QtGui.QIcon(self.ed_class.app.resource_location + '/trash32.png'))
+ pro_grid.addWidget(self.clear_btn, 0, 2)
- self.clear_btn = FCButton(_("Clear"))
- grid_path.addWidget(self.clear_btn, 4, 0, 1, 2)
+ self.add_btn = FCButton(_("Add"))
+ self.add_btn.setIcon(QtGui.QIcon(self.ed_class.app.resource_location + '/plus32.png'))
+ self.editor_vbox.addWidget(self.add_btn)
+ GLay.set_common_column_size([pos_grid, pro_grid], 0)
self.layout.addStretch(1)
diff --git a/appEditors/exc_plugins/ExcSlotArrayPlugin.py b/appEditors/exc_plugins/ExcSlotArrayPlugin.py
index 08250567..4d4a1da8 100644
--- a/appEditors/exc_plugins/ExcSlotArrayPlugin.py
+++ b/appEditors/exc_plugins/ExcSlotArrayPlugin.py
@@ -147,7 +147,7 @@ class ExcSlotArrayEditorUI:
self.copy_tool_box.addLayout(grid0)
# Project distance
- self.project_line_lbl = FCLabel('%s:' % _("Length"))
+ self.project_line_lbl = FCLabel('%s:' % _("Projection"))
self.project_line_lbl.setToolTip(
_("Length of the current segment/move.")
)
diff --git a/appEditors/geo_plugins/GeoCopyPlugin.py b/appEditors/geo_plugins/GeoCopyPlugin.py
index 3f7aa75b..f4445117 100644
--- a/appEditors/geo_plugins/GeoCopyPlugin.py
+++ b/appEditors/geo_plugins/GeoCopyPlugin.py
@@ -147,7 +147,7 @@ class CopyEditorUI:
self.copy_tool_box.addLayout(grid0)
# Project distance
- self.project_line_lbl = FCLabel('%s:' % _("Length"))
+ self.project_line_lbl = FCLabel('%s:' % _("Projection"))
self.project_line_lbl.setToolTip(
_("Length of the current segment/move.")
)
diff --git a/appEditors/geo_plugins/GeoPathPlugin.py b/appEditors/geo_plugins/GeoPathPlugin.py
index b24ddeaa..d1809f0a 100644
--- a/appEditors/geo_plugins/GeoPathPlugin.py
+++ b/appEditors/geo_plugins/GeoPathPlugin.py
@@ -128,7 +128,7 @@ class PathEditorUI:
self.path_tool_box.addLayout(grid_path)
# Project distance
- self.project_line_lbl = FCLabel('%s:' % _("Length"))
+ self.project_line_lbl = FCLabel('%s:' % _("Projection"))
self.project_line_lbl.setToolTip(
_("Length of the current segment/move.")
)
diff --git a/appEditors/geo_plugins/GeoRectanglePlugin.py b/appEditors/geo_plugins/GeoRectanglePlugin.py
index 6815767d..a7e1ae04 100644
--- a/appEditors/geo_plugins/GeoRectanglePlugin.py
+++ b/appEditors/geo_plugins/GeoRectanglePlugin.py
@@ -308,7 +308,7 @@ class RectangleEditorUI:
size_frame.setLayout(size_grid)
# Length
- self.length_lbl = FCLabel('%s:' % _("Length"))
+ self.length_lbl = FCLabel('%s:' % _("Projection"))
self.length_entry = NumericalEvalEntry()
size_grid.addWidget(self.length_lbl, 0, 0)
size_grid.addWidget(self.length_entry, 0, 1)
diff --git a/appGUI/MainGUI.py b/appGUI/MainGUI.py
index a7371e50..dd6fa0a6 100644
--- a/appGUI/MainGUI.py
+++ b/appGUI/MainGUI.py
@@ -4063,16 +4063,12 @@ class MainGUI(QtWidgets.QMainWindow):
and self.app.exc_editor.active_tool.copy_tool.width != 0.0:
pass
else:
- self.app.exc_editor.active_tool.click(
- self.app.geo_editor.snap(self.app.exc_editor.x, self.app.exc_editor.y))
-
- self.app.exc_editor.active_tool.make()
+ curr_pos = self.app.exc_editor.snap_x, self.app.exc_editor.snap_y
+ self.app.exc_editor.on_canvas_click_left_handler(curr_pos)
if self.app.exc_editor.active_tool.complete:
self.app.exc_editor.on_shape_complete()
self.app.inform.emit('[success] %s' % _("Done."))
- # automatically make the selection tool active after completing current action
- self.app.exc_editor.select_tool('select')
# Delete selected object if delete key event comes out of canvas
if key == 'Delete':
diff --git a/appGUI/PlotCanvasLegacy.py b/appGUI/PlotCanvasLegacy.py
index 0df96ebb..56f14be2 100644
--- a/appGUI/PlotCanvasLegacy.py
+++ b/appGUI/PlotCanvasLegacy.py
@@ -29,9 +29,9 @@ try:
# Prevent conflict with Qt5 and above.
from matplotlib import use as mpl_use
- mpl_use("Qt5Agg")
+ mpl_use("QtAgg")
from matplotlib.figure import Figure
- from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
+ from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas
except ImportError:
MATPLOTLIB_AVAILABLE = False
diff --git a/appMain.py b/appMain.py
index 2607d934..37f027ef 100644
--- a/appMain.py
+++ b/appMain.py
@@ -658,7 +658,8 @@ class App(QtCore.QObject):
'del', 'drillcncjob', 'export_dxf', 'edxf', 'export_excellon',
'export_exc',
'export_gcode', 'export_gerber', 'export_svg', 'ext', 'exteriors', 'follow',
- 'geo_union', 'geocutout', 'get_bounds', 'get_names', 'get_path', 'get_sys', 'help',
+ 'geo_union', 'geocutout', 'get_active', 'get_bounds', 'get_names', 'get_path',
+ 'get_sys', 'help',
'interiors', 'isolate', 'join_excellon',
'join_geometry', 'list_sys', 'list_pp', 'milld', 'mills', 'milldrills', 'millslots',
'mirror', 'ncc',
@@ -8456,9 +8457,9 @@ class App(QtCore.QObject):
"script": lambda fname: self.worker_task.emit({'fcn': self.f_handlers.open_script, 'params': [fname]}),
"document": None,
'project': self.f_handlers.open_project,
- 'svg': self.f_handlers.import_svg,
- 'dxf': self.f_handlers.import_dxf,
- 'image': image_opener,
+ 'svg': lambda fname: self.worker_task.emit({'fcn': self.f_handlers.import_svg, 'params': [fname]}),
+ 'dxf': lambda fname: self.worker_task.emit({'fcn': self.f_handlers.import_dxf, 'params': [fname]}),
+ 'image': lambda fname: self.worker_task.emit({'fcn': image_opener, 'params': [fname]}),
'pdf': self.f_handlers.import_pdf
}
diff --git a/appParsers/ParseSVG.py b/appParsers/ParseSVG.py
index 1a8cf402..3c3bafa7 100644
--- a/appParsers/ParseSVG.py
+++ b/appParsers/ParseSVG.py
@@ -478,7 +478,7 @@ def getsvggeo(node, object_type, root=None, units='MM', res=64, factor=1.0, app=
if ref is not None:
geo = getsvggeo(ref, object_type, root=root, units=units, res=res, factor=factor, app=app)
- elif kind in ['defs', 'namedview', 'format', 'type', 'title', 'desc']:
+ elif kind in ['defs', 'namedview', 'format', 'type', 'title', 'desc', 'svg']:
log.warning('SVG Element not supported: %s. Skipping to next.' % kind)
elif kind in ['g']:
diff --git a/appPlugins/ToolImage.py b/appPlugins/ToolImage.py
index c4319618..28d87b2d 100644
--- a/appPlugins/ToolImage.py
+++ b/appPlugins/ToolImage.py
@@ -218,7 +218,7 @@ class ToolImage(AppTool):
else:
self.import_image(filename, import_mode, type_obj, dpi, mode, mask, svg_text, min_area)
- def import_image(self, filename, import_mode, o_type=_("Gerber"), dpi=96, mode='black',
+ def import_image(self, filename, import_mode='raster', o_type=_("Geometry"), dpi=96, mode='black',
mask=None, svg_text=None, min_area=0.0, outname=None, silent=False):
"""
Adds a new Geometry Object to the projects and populates
@@ -331,8 +331,10 @@ class ToolImage(AppTool):
name = outname or filename.split('/')[-1].split('\\')[-1]
units = self.app.app_units
- self.app.app_obj.new_object(obj_type, name, obj_init)
-
+ res = self.app.app_obj.new_object(obj_type, name, obj_init)
+ if res == 'fail':
+ self.app.inform.emit("[ERROR_NOTCL] %s" % _("Failed."))
+ return
# Register recent file
self.app.file_opened.emit("image", filename)
diff --git a/appPlugins/ToolIsolation.py b/appPlugins/ToolIsolation.py
index a7699059..d97be7b0 100644
--- a/appPlugins/ToolIsolation.py
+++ b/appPlugins/ToolIsolation.py
@@ -18,7 +18,6 @@ log = logging.getLogger('base')
class ToolIsolation(AppTool, Gerber):
-
optimal_found_sig = QtCore.pyqtSignal(float)
def __init__(self, app):
@@ -91,6 +90,9 @@ class ToolIsolation(AppTool, Gerber):
self.pool = self.app.pool
self.results = []
+ # store here the validation status (if tool validation is used)
+ self.validation_status = True
+
# disconnect flags
self.area_sel_disconnect_flag = False
self.poly_sel_disconnect_flag = False
@@ -754,7 +756,7 @@ class ToolIsolation(AppTool, Gerber):
new_tools_list = []
if order == 1: # "Forward"
new_tools_list = deepcopy(sorted(self.iso_tools.items(), key=lambda x: x[1]['tooldia'], reverse=False))
- elif order == 2: # "Reverse"
+ elif order == 2: # "Reverse"
new_tools_list = deepcopy(sorted(self.iso_tools.items(), key=lambda x: x[1]['tooldia'], reverse=True))
# clear the tools dictionary
@@ -787,7 +789,7 @@ class ToolIsolation(AppTool, Gerber):
vdia = float(self.ui.tipdia_entry.get_value())
half_vangle = float(self.ui.tipangle_entry.get_value()) / 2
cut_z = self.ui.cutz_entry.get_value()
- cut_z = -cut_z if cut_z < 0 else cut_z # cut_z param has to have a positive value here
+ cut_z = -cut_z if cut_z < 0 else cut_z # cut_z param has to have a positive value here
row = self.ui.tools_table.currentRow()
tool_uid_item = self.ui.tools_table.item(row, 3)
@@ -1021,7 +1023,7 @@ class ToolIsolation(AppTool, Gerber):
def on_toggle_reference(self):
val = self.ui.select_combo.get_value()
- if val == 0: # ALl
+ if val == 0: # ALl
self.ui.reference_combo.hide()
self.ui.reference_combo_type.hide()
self.ui.reference_combo_type_label.hide()
@@ -1059,7 +1061,7 @@ class ToolIsolation(AppTool, Gerber):
self.ui.sel_all_btn.show()
self.ui.clear_all_btn.show()
- else: # Reference Object
+ else: # Reference Object
self.ui.reference_combo.show()
self.ui.reference_combo_type.show()
self.ui.reference_combo_type_label.show()
@@ -1078,7 +1080,7 @@ class ToolIsolation(AppTool, Gerber):
def on_rest_machining_check(self, state):
if state:
- self.ui.iso_order_combo.set_value(2) # "Reverse"
+ self.ui.iso_order_combo.set_value(2) # "Reverse"
self.ui.order_label.setDisabled(True)
self.ui.iso_order_combo.setDisabled(True)
@@ -1119,8 +1121,9 @@ class ToolIsolation(AppTool, Gerber):
min_dict = {}
idx = 1
- for geo in total_geo:
- for s_geo in total_geo[idx:]:
+ w_geo = total_geo.geoms if isinstance(total_geo, (MultiLineString, MultiPolygon)) else total_geo
+ for geo in w_geo:
+ for s_geo in w_geo[idx:]:
# minimize the number of distances by not taking into considerations
# those that are too small
dist = geo.distance(s_geo)
@@ -1141,7 +1144,7 @@ class ToolIsolation(AppTool, Gerber):
min_list = list(min_dict.keys())
min_dist = min(min_list)
- min_dist -= 10**-decimals # make sure that this works for isolation case
+ min_dist -= 10 ** -decimals # make sure that this works for isolation case
return msg, min_dist
@@ -1174,40 +1177,39 @@ class ToolIsolation(AppTool, Gerber):
if res[0] != 'ok':
app_obj.inform.emit(res[0])
return 'fail'
- else:
- min_dist = res[1]
- try:
- min_dist_truncated = self.app.dec_format(float(min_dist), self.decimals)
- self.safe_tooldia = min_dist_truncated
+ min_dist = res[1]
+ min_dist_truncated = self.app.dec_format(float(min_dist), self.decimals)
+ self.safe_tooldia = min_dist_truncated
- if self.safe_tooldia:
- # find the selected tool ID's
- sorted_tools = []
- table_items = self.ui.tools_table.selectedItems()
- sel_rows = {t.row() for t in table_items}
- for row in sel_rows:
- tid = int(self.ui.tools_table.item(row, 3).text())
- sorted_tools.append(tid)
- if not sorted_tools:
- msg = _("There are no tools selected in the Tool Table.")
- self.app.inform.emit('[ERROR_NOTCL] %s' % msg)
- return 'fail'
+ if self.safe_tooldia:
+ # find the selected tool ID's
+ sorted_tools = []
+ table_items = self.ui.tools_table.selectedItems()
+ sel_rows = {t.row() for t in table_items}
+ for row in sel_rows:
+ tid = int(self.ui.tools_table.item(row, 3).text())
+ sorted_tools.append(tid)
+ if not sorted_tools:
+ msg = _("There are no tools selected in the Tool Table.")
+ self.app.inform.emit('[ERROR_NOTCL] %s' % msg)
+ return 'fail'
- # check if the tools diameters are less then the safe tool diameter
- for tool in sorted_tools:
- tool_dia = float(self.iso_tools[tool]['tooldia'])
- if tool_dia > self.safe_tooldia:
- msg = _("Incomplete isolation. "
- "At least one tool could not do a complete isolation.")
- self.app.inform.emit('[WARNING] %s' % msg)
- break
+ # check if the tools diameters are less then the safe tool diameter
+ for tool in sorted_tools:
+ tool_dia = float(self.iso_tools[tool]['tooldia'])
+ if tool_dia > self.safe_tooldia:
+ msg = _("Incomplete isolation. "
+ "At least one tool could not do a complete isolation.")
+ self.app.inform.emit('[WARNING] %s' % msg)
+ # reset the value to prepare for another isolation
+ self.safe_tooldia = None
+ self.validation_status = False
+ return
- # reset the value to prepare for another isolation
- self.safe_tooldia = None
- except Exception as ee:
- self.app.log.error(str(ee))
- return
+ # reset the value to prepare for another isolation
+ self.safe_tooldia = None
+ self.app.inform.emit("Tool validation passed.")
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
@@ -1367,7 +1369,7 @@ class ToolIsolation(AppTool, Gerber):
high_limit = float(db_tool_val['data']['tol_max'])
# we need only tool marked for Isolation Tool
- if db_tool_val['data']['tool_target'] != 3: # _('Isolation')
+ if db_tool_val['data']['tool_target'] != 3: # _('Isolation')
continue
# if we find a tool with the same diameter in the Tools DB just update it's data
@@ -1609,9 +1611,11 @@ class ToolIsolation(AppTool, Gerber):
self.app.worker_task.emit({'fcn': buffer_task, 'params': [self.app]})
def on_iso_button_click(self):
+ use_validation = self.ui.valid_cb.get_value()
+ # assume that the validation is OK
+ self.validation_status = True
self.obj_name = self.ui.object_combo.currentText()
-
# Get source object.
try:
self.grb_obj = self.app.collection.get_by_name(self.obj_name)
@@ -1623,7 +1627,7 @@ class ToolIsolation(AppTool, Gerber):
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), str(self.obj_name)))
return
- if self.ui.valid_cb.get_value() is True:
+ if use_validation is True:
self.find_safe_tooldia_multiprocessing()
def worker_task(iso_class):
@@ -1654,7 +1658,7 @@ class ToolIsolation(AppTool, Gerber):
selection = self.ui.select_combo.get_value()
if selection == 0: # ALL
self.isolate(isolated_obj=isolated_obj, sel_tools=sel_tools, tools_storage=self.iso_tools)
- elif selection == 1: # Area Selection
+ elif selection == 1: # Area Selection
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the start point of the area."))
if self.app.use_3d_engine:
@@ -1673,7 +1677,7 @@ class ToolIsolation(AppTool, Gerber):
# disconnect flags
self.area_sel_disconnect_flag = True
- elif selection == 2: # Polygon Selection
+ elif selection == 2: # Polygon Selection
# disengage the grid snapping since it may be hard to click on polygons with grid snapping on
if self.app.ui.grid_snap_btn.isChecked():
self.grid_status_memory = True
@@ -1694,7 +1698,7 @@ class ToolIsolation(AppTool, Gerber):
# disconnect flags
self.poly_sel_disconnect_flag = True
- elif selection == 3: # Reference Object
+ elif selection == 3: # Reference Object
ref_obj = self.app.collection.get_by_name(self.ui.reference_combo.get_value())
ref_geo = unary_union(ref_obj.solid_geometry)
use_geo = unary_union(isolated_obj.solid_geometry).difference(ref_geo)
@@ -1755,7 +1759,7 @@ class ToolIsolation(AppTool, Gerber):
tools_storage[tool_iso][key]["tools_iso_isoexcept"] = use_iso_except
tools_storage[tool_iso][key]["tools_iso_selection"] = selection_type
tools_storage[tool_iso][key]["tools_iso_area_shape"] = sel_area_shape
- tools_storage[tool_iso][key]["tools_mill_job_type"] = 2 # _("Isolation")
+ tools_storage[tool_iso][key]["tools_mill_job_type"] = 2 # _("Isolation")
tools_storage[tool_iso][key]["tools_mill_tool_shape"] = tool_tip_shape
if use_combine:
@@ -1837,7 +1841,7 @@ class ToolIsolation(AppTool, Gerber):
pad_pass_geo = []
for geo in extra_geo:
iso_offset = tool_dia * ((2 * nr_pass + 1) / 2.0000001) - (
- nr_pass * overlap * tool_dia)
+ nr_pass * overlap * tool_dia)
if negative_dia:
iso_offset = -iso_offset
pad_pass_geo.append(
@@ -1917,11 +1921,14 @@ class ToolIsolation(AppTool, Gerber):
fc_obj.inform.emit(msg)
return 'fail'
else:
- msg = '[success] %s: %s' % (_("Isolation geometry created"), geo_obj.obj_options["name"])
- fc_obj.inform.emit(msg)
+ if self.validation_status:
+ msg = '[success] %s: %s' % (_("Isolation geometry created"),
+ geo_obj.obj_options["name"])
+ fc_obj.inform.emit(msg)
geo_obj.multigeo = True
- self.app.app_obj.new_object("geometry", iso_name, iso_init, plot=plot)
+ a_select = True if self.validation_status else False
+ self.app.app_obj.new_object("geometry", iso_name, iso_init, plot=plot, autoselected=a_select)
# clean the progressive plotted shapes if it was used
@@ -1992,7 +1999,7 @@ class ToolIsolation(AppTool, Gerber):
order = self.ui.iso_order_combo.get_value()
if order == 1: # "Forward"
sorted_tools.sort(reverse=False)
- elif order == 2: # "Reverse"
+ elif order == 2: # "Reverse"
sorted_tools.sort(reverse=True)
else:
pass
@@ -2130,9 +2137,12 @@ class ToolIsolation(AppTool, Gerber):
app_obj.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Empty Geometry in"), geo_obj.obj_options["name"]))
return 'fail'
else:
- app_obj.inform.emit('[success] %s: %s' % (_("Isolation geometry created"), geo_obj.obj_options["name"]))
+ if self.validation_status:
+ msg = '[success] %s: %s' % (_("Isolation geometry created"), geo_obj.obj_options["name"])
+ app_obj.inform.emit(msg)
- self.app.app_obj.new_object("geometry", iso_name, iso_init, plot=plot)
+ a_select = True if self.validation_status else False
+ self.app.app_obj.new_object("geometry", iso_name, iso_init, plot=plot, autoselected=a_select)
# the tools are finished but the isolation is not finished therefore it failed
if work_geo:
@@ -2354,9 +2364,12 @@ class ToolIsolation(AppTool, Gerber):
app_obj.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Empty Geometry in"), geo_obj.obj_options["name"]))
return 'fail'
else:
- app_obj.inform.emit('[success] %s: %s' % (_("Isolation geometry created"), geo_obj.obj_options["name"]))
+ if self.validation_status:
+ msg = '[success] %s: %s' % (_("Isolation geometry created"), geo_obj.obj_options["name"])
+ app_obj.inform.emit(msg)
- self.app.app_obj.new_object("geometry", iso_name, iso_init, plot=plot)
+ a_select = True if self.validation_status else False
+ self.app.app_obj.new_object("geometry", iso_name, iso_init, plot=plot, autoselected=a_select)
def area_subtraction(self, geo, subtraction_geo=None):
"""
@@ -2956,7 +2969,7 @@ class ToolIsolation(AppTool, Gerber):
"""
tool_from_db = deepcopy(tool)
- if tool['data']['tool_target'] not in [0, 3]: # [General, Isolation]
+ if tool['data']['tool_target'] not in [0, 3]: # [General, Isolation]
for idx in range(self.app.ui.plot_tab_area.count()):
if self.app.ui.plot_tab_area.tabText(idx) == _("Tools Database"):
wdg = self.app.ui.plot_tab_area.widget(idx)
@@ -3215,7 +3228,7 @@ class ToolIsolation(AppTool, Gerber):
if isinstance(temp_geo, Polygon):
# calculate the number of subgeos in the buffered geo
- temp_geo_len = len([1] + list(temp_geo.interiors)) # one exterior + interiors
+ temp_geo_len = len([1] + list(temp_geo.interiors)) # one exterior + interiors
if total_poly_len != temp_geo_len:
# some interiors could not be isolated
break
@@ -3330,7 +3343,6 @@ class ToolIsolation(AppTool, Gerber):
class IsoUI:
-
pluginName = _("Isolation")
def __init__(self, layout, app):
diff --git a/appPlugins/ToolSolderPaste.py b/appPlugins/ToolSolderPaste.py
index 98872a84..dfd1356f 100644
--- a/appPlugins/ToolSolderPaste.py
+++ b/appPlugins/ToolSolderPaste.py
@@ -107,7 +107,7 @@ class SolderPaste(AppTool):
self.set_tool_ui()
self.build_ui()
- self.app.ui.notebook.setTabText(2, _("SP Dispenser"))
+ self.app.ui.notebook.setTabText(2, _("SolderPaste"))
def install(self, icon=None, separator=None, **kwargs):
AppTool.install(self, icon, separator, shortcut='Alt+K', **kwargs)
@@ -1226,7 +1226,7 @@ class SolderPaste(AppTool):
class SolderUI:
- pluginName = _("SP Dispenser")
+ pluginName = _("SolderPaste")
def __init__(self, layout, app, solder_class):
self.app = app
diff --git a/tclCommands/TclCommandGetActive.py b/tclCommands/TclCommandGetActive.py
new file mode 100644
index 00000000..7815ab8b
--- /dev/null
+++ b/tclCommands/TclCommandGetActive.py
@@ -0,0 +1,50 @@
+
+from tclCommands.TclCommand import TclCommand
+
+import collections
+
+
+class TclCommandGetActive(TclCommand):
+ """
+ Tcl shell command to get the current active object name.
+
+ example:
+
+ """
+
+ # List of all command aliases, to be able to use old names for backward compatibility (add_poly, add_polygon)
+ aliases = ['get_active']
+
+ description = '%s %s' % ("--", "Gets the active (selected) application object name.")
+
+ # Dictionary of types from Tcl command, needs to be ordered
+ arg_names = collections.OrderedDict([
+ ])
+
+ # Dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value
+ option_types = collections.OrderedDict([
+ ])
+
+ # array of mandatory options for current Tcl command: required = {'name','outname'}
+ required = []
+
+ # structured help for current command, args needs to be ordered
+ help = {
+ 'main': 'Gets the active (selected) application object name.',
+ 'args': collections.OrderedDict([
+ ]),
+ 'examples': ['get_active']
+ }
+
+ def execute(self, args, unnamed_args):
+ """
+
+ :param args:
+ :param unnamed_args:
+ :return:
+ """
+
+ try:
+ return self.app.collection.get_active().options['name']
+ except Exception as e:
+ return "Command failed: %s" % str(e)
diff --git a/tclCommands/__init__.py b/tclCommands/__init__.py
index 91cc7718..23f65da3 100644
--- a/tclCommands/__init__.py
+++ b/tclCommands/__init__.py
@@ -29,6 +29,7 @@ import tclCommands.TclCommandExteriors
import tclCommands.TclCommandFollow
import tclCommands.TclCommandGeoCutout
import tclCommands.TclCommandGeoUnion
+import tclCommands.TclCommandGetActive
import tclCommands.TclCommandGetNames
import tclCommands.TclCommandGetPath
import tclCommands.TclCommandGetSys