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