From 942d9e2b0184cda1197a9e3623eb2b7e3474fb1f Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Wed, 10 Apr 2019 03:56:56 +0300 Subject: [PATCH] - Gerber Editor: finished the selection on canvas; should be used as an template for the other Editors - Gerber Editor: finished the Copy, Aperture Add, Buffer, Scale, Move including the Utility geometry - Trying to fix bug in Measurement Tool: the mouse events don't disconnect --- FlatCAMApp.py | 2 +- README.md | 3 + flatcamEditors/FlatCAMExcEditor.py | 2 +- flatcamEditors/FlatCAMGeoEditor.py | 2 +- flatcamEditors/FlatCAMGrbEditor.py | 662 ++++++++++------------------- flatcamGUI/FlatCAMGUI.py | 24 +- flatcamGUI/PlotCanvas.py | 7 +- flatcamTools/ToolMeasurement.py | 263 +++++------- 8 files changed, 356 insertions(+), 609 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 8ee10335..afc5069b 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -1220,7 +1220,7 @@ class App(QtCore.QObject): # self.ui.options_scroll_area.verticalScrollBar().sizeHint().width()) self.collection.view.setMinimumWidth(290) - self.log.debug("Finished adding Geometry and Excellon Editor's.") + self.log.debug("Finished adding FlatCAM Editor's.") #### Worker #### if self.defaults["global_worker_number"]: diff --git a/README.md b/README.md index a1a8153e..aad946de 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,9 @@ CAD program, and create G-Code for Isolation routing. - Gerber Editor: added buffer and scale tools - Gerber Editor: working on aperture selection to show on Aperture Table +- Gerber Editor: finished the selection on canvas; should be used as an template for the other Editors +- Gerber Editor: finished the Copy, Aperture Add, Buffer, Scale, Move including the Utility geometry +- Trying to fix bug in Measurement Tool: the mouse events don't disconnect 7.04.2019 diff --git a/flatcamEditors/FlatCAMExcEditor.py b/flatcamEditors/FlatCAMExcEditor.py index 9a793a29..41c1be95 100644 --- a/flatcamEditors/FlatCAMExcEditor.py +++ b/flatcamEditors/FlatCAMExcEditor.py @@ -1687,7 +1687,7 @@ class FlatCAMExcEditor(QtCore.QObject): self.new_tools = {} self.new_drills = [] - self.storage_dict = {} + # self.storage_dict = {} self.shapes.clear(update=True) self.tool_shape.clear(update=True) diff --git a/flatcamEditors/FlatCAMGeoEditor.py b/flatcamEditors/FlatCAMGeoEditor.py index 16e9d085..3caed9bb 100644 --- a/flatcamEditors/FlatCAMGeoEditor.py +++ b/flatcamEditors/FlatCAMGeoEditor.py @@ -3024,7 +3024,7 @@ class FlatCAMGeoEditor(QtCore.QObject): self.shapes.clear(update=True) self.tool_shape.clear(update=True) - self.storage = FlatCAMGeoEditor.make_storage() + # self.storage = FlatCAMGeoEditor.make_storage() self.replot() def edit_fcgeometry(self, fcgeometry, multigeo_tool=None): diff --git a/flatcamEditors/FlatCAMGrbEditor.py b/flatcamEditors/FlatCAMGrbEditor.py index 3ced3846..193954fe 100644 --- a/flatcamEditors/FlatCAMGrbEditor.py +++ b/flatcamEditors/FlatCAMGrbEditor.py @@ -191,109 +191,6 @@ class FCBuffer(FCShapeTool): self.deactivate() -class FCApertureResize(FCShapeTool): - def __init__(self, draw_app): - DrawTool.__init__(self, draw_app) - self.name = 'aperture_resize' - - self.draw_app.app.inform.emit(_("Click on the Apertures to resize ...")) - self.resize_dia = None - self.draw_app.resize_frame.show() - self.points = None - self.selected_dia_list = [] - self.current_storage = None - self.geometry = [] - self.destination_storage = None - - self.draw_app.resize_btn.clicked.connect(self.make) - - # Switch notebook to Selected page - self.draw_app.app.ui.notebook.setCurrentWidget(self.draw_app.app.ui.selected_tab) - - def make(self): - self.draw_app.is_modified = True - - try: - new_dia = self.draw_app.resdrill_entry.get_value() - except: - self.draw_app.app.inform.emit(_("[ERROR_NOTCL] Resize drill(s) failed. Please enter a diameter for resize.")) - return - - if new_dia not in self.draw_app.olddia_newdia: - self.destination_storage = FlatCAMGeoEditor.make_storage() - self.draw_app.storage_dict[new_dia] = self.destination_storage - - # self.olddia_newdia dict keeps the evidence on current tools diameters as keys and gets updated on values - # each time a tool diameter is edited or added - self.draw_app.olddia_newdia[new_dia] = new_dia - else: - self.destination_storage = self.draw_app.storage_dict[new_dia] - - for index in self.draw_app.apertures_table.selectedIndexes(): - row = index.row() - # on column 1 in tool tables we hold the diameters, and we retrieve them as strings - # therefore below we convert to float - dia_on_row = self.draw_app.apertures_table.item(row, 1).text() - self.selected_dia_list.append(float(dia_on_row)) - - # since we add a new tool, we update also the intial state of the tool_table through it's dictionary - # we add a new entry in the tool2tooldia dict - self.draw_app.tool2tooldia[len(self.draw_app.olddia_newdia)] = new_dia - - sel_shapes_to_be_deleted = [] - - for sel_dia in self.selected_dia_list: - self.current_storage = self.draw_app.storage_dict[sel_dia] - for select_shape in self.draw_app.get_selected(): - if select_shape in self.current_storage.get_objects(): - factor = new_dia / sel_dia - self.geometry.append( - DrawToolShape(affinity.scale(select_shape.geo, xfact=factor, yfact=factor, origin='center')) - ) - self.current_storage.remove(select_shape) - # a hack to make the tool_table display less drills per diameter when shape(drill) is deleted - # self.points_edit it's only useful first time when we load the data into the storage - # but is still used as reference when building tool_table in self.build_ui() - # the number of drills displayed in column 2 is just a len(self.points_edit) therefore - # deleting self.points_edit elements (doesn't matter who but just the number) - # solved the display issue. - del self.draw_app.points_edit[sel_dia][0] - - sel_shapes_to_be_deleted.append(select_shape) - - self.draw_app.on_exc_shape_complete(self.destination_storage) - # a hack to make the tool_table display more drills per diameter when shape(drill) is added - # self.points_edit it's only useful first time when we load the data into the storage - # but is still used as reference when building tool_table in self.build_ui() - # the number of drills displayed in column 2 is just a len(self.points_edit) therefore - # deleting self.points_edit elements (doesn't matter who but just the number) - # solved the display issue. - if new_dia not in self.draw_app.points_edit: - self.draw_app.points_edit[new_dia] = [(0, 0)] - else: - self.draw_app.points_edit[new_dia].append((0,0)) - self.geometry = [] - - # if following the resize of the drills there will be no more drills for the selected tool then - # delete that tool - if not self.draw_app.points_edit[sel_dia]: - self.draw_app.on_aperture_delete(sel_dia) - - for shp in sel_shapes_to_be_deleted: - self.draw_app.selected.remove(shp) - sel_shapes_to_be_deleted = [] - - self.draw_app.build_ui() - self.draw_app.plot_all() - - self.draw_app.resize_frame.hide() - self.complete = True - self.draw_app.app.inform.emit(_("[success] Done. Drill Resize completed.")) - - # MS: always return to the Select Tool - self.draw_app.select_tool("select") - - class FCApertureMove(FCShapeTool): def __init__(self, draw_app): DrawTool.__init__(self, draw_app) @@ -302,7 +199,7 @@ class FCApertureMove(FCShapeTool): # self.shape_buffer = self.draw_app.shape_buffer self.origin = None self.destination = None - self.selected_dia_list = [] + self.selected_apertures = [] if self.draw_app.launched_from_shortcuts is True: self.draw_app.launched_from_shortcuts = False @@ -316,8 +213,8 @@ class FCApertureMove(FCShapeTool): row = index.row() # on column 1 in tool tables we hold the diameters, and we retrieve them as strings # therefore below we convert to float - dia_on_row = self.draw_app.apertures_table.item(row, 1).text() - self.selected_dia_list.append(float(dia_on_row)) + aperture_on_row = self.draw_app.apertures_table.item(row, 1).text() + self.selected_apertures.append(aperture_on_row) # Switch notebook to Selected page self.draw_app.app.ui.notebook.setCurrentWidget(self.draw_app.app.ui.selected_tab) @@ -347,15 +244,15 @@ class FCApertureMove(FCShapeTool): dy = self.destination[1] - self.origin[1] sel_shapes_to_be_deleted = [] - for sel_dia in self.selected_dia_list: - self.current_storage = self.draw_app.storage_dict[sel_dia] + for sel_dia in self.selected_apertures: + self.current_storage = self.draw_app.storage_dict[sel_dia]['solid_geometry'] for select_shape in self.draw_app.get_selected(): - if select_shape in self.current_storage.get_objects(): + if select_shape in self.current_storage: self.geometry.append(DrawToolShape(affinity.translate(select_shape.geo, xoff=dx, yoff=dy))) self.current_storage.remove(select_shape) sel_shapes_to_be_deleted.append(select_shape) - self.draw_app.on_exc_shape_complete(self.current_storage) + self.draw_app.on_grb_shape_complete(self.current_storage) self.geometry = [] for shp in sel_shapes_to_be_deleted: @@ -363,7 +260,7 @@ class FCApertureMove(FCShapeTool): sel_shapes_to_be_deleted = [] self.draw_app.build_ui() - self.draw_app.app.inform.emit(_("[success] Done. Drill(s) Move completed.")) + self.draw_app.app.inform.emit(_("[success] Done. Apertures Move completed.")) def utility_geometry(self, data=None): """ @@ -398,17 +295,14 @@ class FCApertureCopy(FCApertureMove): dy = self.destination[1] - self.origin[1] sel_shapes_to_be_deleted = [] - for sel_dia in self.selected_dia_list: - self.current_storage = self.draw_app.storage_dict[sel_dia] + for sel_dia in self.selected_apertures: + self.current_storage = self.draw_app.storage_dict[sel_dia]['solid_geometry'] for select_shape in self.draw_app.get_selected(): - if select_shape in self.current_storage.get_objects(): + if select_shape in self.current_storage: self.geometry.append(DrawToolShape(affinity.translate(select_shape.geo, xoff=dx, yoff=dy))) - # add some fake drills into the self.draw_app.points_edit to update the drill count in tool table - self.draw_app.points_edit[sel_dia].append((0, 0)) - sel_shapes_to_be_deleted.append(select_shape) - self.draw_app.on_exc_shape_complete(self.current_storage) + self.draw_app.on_grb_shape_complete(self.current_storage) self.geometry = [] for shp in sel_shapes_to_be_deleted: @@ -416,7 +310,7 @@ class FCApertureCopy(FCApertureMove): sel_shapes_to_be_deleted = [] self.draw_app.build_ui() - self.draw_app.app.inform.emit(_("[success] Done. Drill(s) copied.")) + self.draw_app.app.inform.emit(_("[success] Done. Apertures copied.")) class FCApertureSelect(DrawTool): @@ -431,6 +325,7 @@ class FCApertureSelect(DrawTool): # here we store all shapes that were selected self.sel_storage = [] + self.grb_editor_app.apertures_table.clearSelection() self.grb_editor_app.hide_tool('all') self.grb_editor_app.hide_tool('select') @@ -453,68 +348,33 @@ class FCApertureSelect(DrawTool): for storage in self.grb_editor_app.storage_dict: for shape in self.grb_editor_app.storage_dict[storage]['solid_geometry']: if Point(point).within(shape.geo): - sel_aperture.add(storage) + if self.draw_app.key == self.draw_app.app.defaults["global_mselect_key"]: + if shape in self.draw_app.selected: + self.draw_app.selected.remove(shape) + else: + # add the object to the selected shapes + self.draw_app.selected.append(shape) + sel_aperture.add(storage) + else: + self.draw_app.selected.append(shape) + sel_aperture.add(storage) + try: - self.grb_editor_app.apertures_table.itemClicked.disconnect() + self.draw_app.apertures_table.cellPressed.disconnect() except: pass + + self.grb_editor_app.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection) for aper in sel_aperture: for row in range(self.grb_editor_app.apertures_table.rowCount()): if str(aper) == self.grb_editor_app.apertures_table.item(row, 1).text(): self.grb_editor_app.apertures_table.selectRow(row) - return "" + self.draw_app.last_aperture_selected = aper + self.grb_editor_app.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) - # def select_shapes(self, pos): - # self.grb_editor_app.apertures_table.clearSelection() - # - # for storage in self.grb_editor_app.storage_dict: - # for shape in self.grb_editor_app.storage_dict[storage]['solid_geometry']: - # if Point(pos).within(shape.geo): - # self.sel_storage.append(shape) - # xmin, ymin, xmax, ymax = self.bounds(self.sel_storage) - # - # if pos[0] < xmin or pos[0] > xmax or pos[1] < ymin or pos[1] > ymax: - # self.grb_editor_app.selected = [] - # else: - # key_modifier = QtWidgets.QApplication.keyboardModifiers() - # if self.grb_editor_app.app.defaults["global_mselect_key"] == 'Control': - # # if CONTROL 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 == Qt.ControlModifier: - # if closest_shape in self.grb_editor_app.selected: - # self.grb_editor_app.selected.remove(closest_shape) - # else: - # self.grb_editor_app.selected.append(closest_shape) - # else: - # self.grb_editor_app.selected = [] - # self.grb_editor_app.selected.append(closest_shape) - # else: - # if key_modifier == Qt.ShiftModifier: - # if closest_shape in self.grb_editor_app.selected: - # self.grb_editor_app.selected.remove(closest_shape) - # else: - # self.grb_editor_app.selected.append(closest_shape) - # else: - # self.grb_editor_app.selected = [] - # self.grb_editor_app.selected.append(closest_shape) - # - # # select the aperture of the selected shape in the tool table - # for storage in self.grb_editor_app.storage_dict: - # for shape_s in self.grb_editor_app.selected: - # if shape_s in self.grb_editor_app.storage_dict[storage]: - # for key in self.grb_editor_app.tool2tooldia: - # if self.grb_editor_app.tool2tooldia[key] == storage: - # item = self.grb_editor_app.apertures_table.item((key - 1), 1) - # self.grb_editor_app.apertures_table.setCurrentItem(item) - # # item.setSelected(True) - # # self.grb_editor_app.apertures_table.selectItem(key - 1) - # # midx = self.grb_editor_app.apertures_table.model().index((key - 1), 0) - # # self.grb_editor_app.apertures_table.setCurrentIndex(midx) - # self.draw_app.last_tool_selected = key - # # delete whatever is in selection storage, there is no longer need for those shapes - # self.sel_storage = [] - # - # return "" + self.draw_app.apertures_table.cellPressed.connect(self.draw_app.on_row_selected) + + return "" class FlatCAMGrbEditor(QtCore.QObject): @@ -619,9 +479,9 @@ class FlatCAMGrbEditor(QtCore.QObject): ) grid1.addWidget(apcode_lbl, 1, 0) - self.apcodeentry = FCEntry() - self.apcodeentry.setValidator(QtGui.QIntValidator(0,999)) - grid1.addWidget(self.apcodeentry, 1, 1) + self.apcode_entry = FCEntry() + self.apcode_entry.setValidator(QtGui.QIntValidator(0, 999)) + grid1.addWidget(self.apcode_entry, 1, 1) apsize_lbl = QtWidgets.QLabel(_('Aperture Size:')) apsize_lbl.setToolTip( @@ -772,12 +632,10 @@ class FlatCAMGrbEditor(QtCore.QObject): "constructor": FCBuffer}, "aperture_scale": {"button": self.app.ui.aperture_scale_btn, "constructor": FCScale}, - # "aperture_resize": {"button": self.app.ui.resize_aperture_btn, - # "constructor": FCApertureResize}, - # "ap_geometry_copy": {"button": self.app.ui.copy_ap_geometry_btn, - # "constructor": FCApertureCopy}, - # "ap_geometry_move": {"button": self.app.ui.move_drill_btn, - # "constructor": FCApertureMove}, + "aperture_copy": {"button": self.app.ui.aperture_copy_btn, + "constructor": FCApertureCopy}, + "aperture_move": {"button": self.app.ui.aperture_move_btn, + "constructor": FCApertureMove}, } ### Data @@ -822,13 +680,12 @@ class FlatCAMGrbEditor(QtCore.QObject): self.addaperture_btn.clicked.connect(self.on_aperture_add) self.delaperture_btn.clicked.connect(self.on_aperture_delete) - self.apertures_table.selectionModel().currentChanged.connect(self.on_row_selected) + self.apertures_table.cellPressed.connect(self.on_row_selected) - self.app.ui.grb_resize_aperture_menuitem.triggered.connect(self.exc_resize_drills) - self.app.ui.grb_copy_menuitem.triggered.connect(self.exc_copy_drills) + self.app.ui.grb_copy_menuitem.triggered.connect(self.on_copy_button) self.app.ui.grb_delete_menuitem.triggered.connect(self.on_delete_btn) - self.app.ui.grb_move_menuitem.triggered.connect(self.exc_move_drills) + self.app.ui.grb_move_menuitem.triggered.connect(self.on_move_button) # Init GUI @@ -936,6 +793,11 @@ class FlatCAMGrbEditor(QtCore.QObject): except: pass + try: + self.apertures_table.cellPressed.disconnect() + except: + pass + # updated units self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper() @@ -1066,6 +928,7 @@ class FlatCAMGrbEditor(QtCore.QObject): # we reactivate the signals after the after the tool adding as we don't need to see the tool been populated self.apertures_table.itemChanged.connect(self.on_tool_edit) + self.apertures_table.cellPressed.connect(self.on_row_selected) def on_aperture_add(self, apid=None): self.is_modified = True @@ -1073,14 +936,42 @@ class FlatCAMGrbEditor(QtCore.QObject): ap_id = apid else: try: - ap_id = str(self.apsize_entry.get_value()) + ap_id = str(self.apcode_entry.get_value()) except ValueError: + self.app.inform.emit(_("[WARNING_NOTCL] Aperture code value is missing or wrong format. " + "Add it and retry.")) return if ap_id not in self.olddia_newdia: self.storage_dict[ap_id] = {} - self.storage_dict[ap_id]['type'] = 'C' - self.storage_dict[ap_id]['size'] = 1 + + type_val = self.aptype_cb.currentText() + self.storage_dict[ap_id]['type'] = type_val + try: + size_val = float(self.apsize_entry.get_value()) + except ValueError: + # try to convert comma to decimal point. if it's still not working error message and return + try: + size_val = float(self.apsize_entry.get_value().replace(',', '.')) + self.apsize_entry.set_value(size_val) + except ValueError: + self.app.inform.emit(_("[WARNING_NOTCL] Aperture size value is missing or wrong format. " + "Add it and retry.")) + return + self.storage_dict[ap_id]['size'] = size_val + + if type_val == 'R': + try: + dims = self.apdim_entry.get_value() + self.storage_dict[ap_id]['width'] = dims[0] + self.storage_dict[ap_id]['height'] = dims[1] + except Exception as e: + log.error("FlatCAMGrbEditor.on_aperture_add() --> the R aperture dims has to be in a " + "tuple format (x,y)\nError: %s" % str(e)) + self.app.inform.emit(_("[WARNING_NOTCL] Aperture dimensions value is missing or wrong format. " + "Add it in format (width, height) and retry.")) + return + self.storage_dict[ap_id]['solid_geometry'] = [] self.storage_dict[ap_id]['follow_geometry'] = [] @@ -1088,16 +979,14 @@ class FlatCAMGrbEditor(QtCore.QObject): # each time a tool diameter is edited or added self.olddia_newdia[ap_id] = ap_id else: - self.app.inform.emit(_("[WARNING_NOTCL] Tool already in the original or actual tool list.\n" - "Save and reedit Excellon if you need to add this tool. ") - ) + self.app.inform.emit(_("[WARNING_NOTCL] Aperture already in the aperture table.")) return # since we add a new tool, we update also the initial state of the tool_table through it's dictionary # we add a new entry in the tool2tooldia dict self.tool2tooldia[len(self.olddia_newdia)] = ap_id - self.app.inform.emit(_("[success] Added new tool with dia: {apid}").format(apid=str(ap_id))) + self.app.inform.emit(_("[success] Added new aperture with dia: {apid}").format(apid=str(ap_id))) self.build_ui() @@ -1159,7 +1048,7 @@ class FlatCAMGrbEditor(QtCore.QObject): # if connected, disconnect the signal from the slot on item_changed as it creates issues self.apertures_table.itemChanged.disconnect() - # self.apertures_table.selectionModel().currentChanged.disconnect() + # self.apertures_table.cellPressed.disconnect() self.is_modified = True geometry = [] @@ -1209,7 +1098,7 @@ class FlatCAMGrbEditor(QtCore.QObject): # we reactivate the signals after the after the tool editing self.apertures_table.itemChanged.connect(self.on_tool_edit) - # self.apertures_table.selectionModel().currentChanged.connect(self.on_row_selected) + # self.apertures_table.cellPressed.connect(self.on_row_selected) def on_name_activate(self): self.edited_obj_name = self.name_entry.get_value() @@ -1341,14 +1230,9 @@ class FlatCAMGrbEditor(QtCore.QObject): # self.shape_buffer = [] self.selected = [] - self.storage_dict = {} - self.shapes.clear(update=True) self.tool_shape.clear(update=True) - # self.storage = FlatCAMExcEditor.make_storage() - self.plot_all() - def edit_fcgerber(self, orig_grb_obj): """ Imports the geometry found in self.apertures from the given FlatCAM Gerber object @@ -1444,6 +1328,7 @@ class FlatCAMGrbEditor(QtCore.QObject): # reset the tool table self.apertures_table.clear() + self.apertures_table.setHorizontalHeaderLabels(['#', _('Code'), _('Type'), _('Size'), _('Dim')]) self.last_aperture_selected = None @@ -1481,14 +1366,15 @@ class FlatCAMGrbEditor(QtCore.QObject): self.gerber_obj.options['name'].upper()) out_name = outname + local_storage_dict = deepcopy(self.storage_dict) # How the object should be initialized def obj_init(grb_obj, app_obj): + poly_buffer = [] follow_buffer = [] - new_geo = [] - for storage_apid, storage_val in self.storage_dict.items(): + for storage_apid, storage_val in local_storage_dict.items(): grb_obj.apertures[storage_apid] = {} for k, v in storage_val.items(): @@ -1565,7 +1451,7 @@ class FlatCAMGrbEditor(QtCore.QObject): # self.draw_app.select_tool('select') self.complete = True current_tool = 'select' - self.app.inform.emit(_("[WARNING_NOTCL] Cancelled. There is no Tool/Drill selected")) + self.app.inform.emit(_("[WARNING_NOTCL] Cancelled. No aperture is selected")) # This is to make the group behave as radio group if current_tool in self.tools_gerber: @@ -1584,87 +1470,39 @@ class FlatCAMGrbEditor(QtCore.QObject): self.tools_gerber[t]["button"].setChecked(False) self.active_tool = None - def on_row_selected(self): - self.selected = [] + def on_row_selected(self, row, col): + if col == 0: + key_modifier = QtWidgets.QApplication.keyboardModifiers() + if self.app.defaults["global_mselect_key"] == 'Control': + modifier_to_use = Qt.ControlModifier + else: + modifier_to_use = Qt.ShiftModifier - try: - selected_dia = self.tool2tooldia[self.apertures_table.currentRow() + 1] - self.last_aperture_selected = self.apertures_table.currentRow() + 1 - for obj in self.storage_dict[selected_dia].get_objects(): - self.selected.append(obj) - except Exception as e: - self.app.log.debug(str(e)) + if key_modifier == modifier_to_use: + pass + else: + self.selected = [] - self.plot_all() + try: + selected_apid = str(self.tool2tooldia[row + 1]) + self.last_aperture_selected = row + 1 + + for obj in self.storage_dict[selected_apid]['solid_geometry']: + self.selected.append(obj) + except Exception as e: + self.app.log.debug(str(e)) + + self.plot_all() def toolbar_tool_toggle(self, key): self.options[key] = self.sender().isChecked() - if self.options[key] == True: - return 1 - else: - return 0 + return self.options[key] - def on_canvas_click(self, event): - """ - event.x and .y have canvas coordinates - event.xdaya and .ydata have plot coordinates - - :param event: Event object dispatched by Matplotlib - :return: None - """ - - if event.button is 1: - self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " - "%.4f    " % (0, 0)) - self.pos = self.canvas.vispy_canvas.translate_coords(event.pos) - - ### Snap coordinates - x, y = self.app.geo_editor.snap(self.pos[0], self.pos[1]) - - self.pos = (x, y) - # print(self.active_tool) - - # Selection with left mouse button - if self.active_tool is not None and event.button is 1: - # Dispatch event to active_tool - # msg = self.active_tool.click(self.app.geo_editor.snap(event.xdata, event.ydata)) - msg = self.active_tool.click(self.app.geo_editor.snap(self.pos[0], self.pos[1])) - - # 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() - # 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.defaults["global_mselect_key"] == 'Control': - modifier_to_use = Qt.ControlModifier - else: - modifier_to_use = Qt.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 key_modifier == modifier_to_use: - self.select_tool(self.active_tool.name) - else: - self.select_tool("select") - return - - if isinstance(self.active_tool, FCApertureSelect): - # self.app.log.debug("Replotting after click.") - self.plot_all() - else: - self.app.log.debug("No active tool to respond to click!") - - def on_exc_shape_complete(self, storage): + def on_grb_shape_complete(self, storage): self.app.log.debug("on_shape_complete()") # Add shape - if type(storage) is list: - for item_storage in storage: - self.add_gerber_shape(self.active_tool.geometry, item_storage) - else: - self.add_gerber_shape(self.active_tool.geometry, storage) + self.add_gerber_shape(self.active_tool.geometry, storage) # Remove any utility shapes self.delete_utility_geometry() @@ -1672,7 +1510,6 @@ class FlatCAMGrbEditor(QtCore.QObject): # Replot and reset tool. self.plot_all() - # self.active_tool = type(self.active_tool)(self) def add_gerber_shape(self, shape, storage): """ @@ -1703,35 +1540,56 @@ class FlatCAMGrbEditor(QtCore.QObject): else: storage.append(shape) # TODO: Check performance - def add_shape(self, shape): + def on_canvas_click(self, event): """ - Adds a shape to the shape storage. + event.x and .y have canvas coordinates + event.xdaya and .ydata have plot coordinates - :param shape: Shape to be added. - :type shape: DrawToolShape + :param event: Event object dispatched by Matplotlib :return: None """ - # List of DrawToolShape? - if isinstance(shape, list): - for subshape in shape: - self.add_shape(subshape) - return + if event.button is 1: + self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " + "%.4f    " % (0, 0)) + self.pos = self.canvas.vispy_canvas.translate_coords(event.pos) - assert isinstance(shape, DrawToolShape), \ - "Expected a DrawToolShape, got %s" % type(shape) + ### Snap coordinates + x, y = self.app.geo_editor.snap(self.pos[0], self.pos[1]) - assert shape.geo is not None, \ - "Shape object has empty geometry (None)" + self.pos = (x, y) - assert (isinstance(shape.geo, list) and len(shape.geo) > 0) or \ - not isinstance(shape.geo, list), \ - "Shape objects has empty geometry ([])" + # Selection with left mouse button + if self.active_tool is not None and event.button is 1: + # Dispatch event to active_tool + # msg = self.active_tool.click(self.app.geo_editor.snap(event.xdata, event.ydata)) + msg = self.active_tool.click(self.app.geo_editor.snap(self.pos[0], self.pos[1])) - if isinstance(shape, DrawToolUtilityShape): - self.utility.append(shape) - else: - self.storage.insert(shape) # TODO: Check performance + # 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_grb_shape_complete(self.current_storage) + self.build_ui() + # 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.defaults["global_mselect_key"] == 'Control': + modifier_to_use = Qt.ControlModifier + else: + modifier_to_use = Qt.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 key_modifier == modifier_to_use: + self.select_tool(self.active_tool.name) + else: + self.select_tool("select") + return + + if isinstance(self.active_tool, FCApertureSelect): + # self.app.log.debug("Replotting after click.") + self.plot_all() + else: + self.app.log.debug("No active tool to respond to click!") def on_canvas_click_release(self, event): pos_canvas = self.canvas.vispy_canvas.translate_coords(event.pos) @@ -1763,13 +1621,17 @@ class FlatCAMGrbEditor(QtCore.QObject): if self.app.selection_type is not None: self.draw_selection_area_handler(self.pos, pos, self.app.selection_type) self.app.selection_type = None + elif isinstance(self.active_tool, FCApertureSelect): # Dispatch event to active_tool # msg = self.active_tool.click(self.app.geo_editor.snap(event.xdata, event.ydata)) # msg = self.active_tool.click_release((self.pos[0], self.pos[1])) # self.app.inform.emit(msg) self.active_tool.click_release((self.pos[0], self.pos[1])) - self.plot_all() + + # if there are selected objects then plot them + if self.selected: + self.plot_all() except Exception as e: log.warning("Error: %s" % str(e)) raise @@ -1784,9 +1646,12 @@ class FlatCAMGrbEditor(QtCore.QObject): """ poly_selection = Polygon([start_pos, (end_pos[0], start_pos[1]), end_pos, (start_pos[0], end_pos[1])]) + sel_aperture = set() + self.apertures_table.clearSelection() + self.app.delete_selection_shape() for storage in self.storage_dict: - for obj in self.storage_dict[storage].get_objects(): + for obj in self.storage_dict[storage]['solid_geometry']: if (sel_type is True and poly_selection.contains(obj.geo)) or \ (sel_type is False and poly_selection.intersects(obj.geo)): if self.key == self.app.defaults["global_mselect_key"]: @@ -1795,21 +1660,25 @@ class FlatCAMGrbEditor(QtCore.QObject): else: # add the object to the selected shapes self.selected.append(obj) + sel_aperture.add(storage) else: self.selected.append(obj) + sel_aperture.add(storage) - # select the diameter of the selected shape in the tool table - for storage in self.storage_dict: - for shape_s in self.selected: - if shape_s in self.storage_dict[storage].get_objects(): - for key in self.tool2tooldia: - if self.tool2tooldia[key] == storage: - item = self.apertures_table.item((key - 1), 1) - self.apertures_table.setCurrentItem(item) - self.last_aperture_selected = key - # item.setSelected(True) - # self.grb_editor_app.apertures_table.selectItem(key - 1) + try: + self.apertures_table.cellPressed.disconnect() + except: + pass + self.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection) + for aper in sel_aperture: + for row in range(self.apertures_table.rowCount()): + if str(aper) == self.apertures_table.item(row, 1).text(): + self.apertures_table.selectRow(row) + self.last_aperture_selected = aper + self.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) + + self.apertures_table.cellPressed.connect(self.on_row_selected) self.plot_all() def on_canvas_move(self, event): @@ -1898,42 +1767,19 @@ class FlatCAMGrbEditor(QtCore.QObject): self.key = None def draw_utility_geometry(self, geo): - # Add the new utility shape - try: - # this case is for the Font Parse - for el in list(geo.geo): - if type(el) == MultiPolygon: - for poly in el: - self.tool_shape.add( - shape=poly, - color=(self.app.defaults["global_draw_color"] + '80'), - update=False, - layer=0, - tolerance=None - ) - elif type(el) == MultiLineString: - for linestring in el: - self.tool_shape.add( - shape=linestring, - color=(self.app.defaults["global_draw_color"] + '80'), - update=False, - layer=0, - tolerance=None - ) - else: - self.tool_shape.add( - shape=el, - color=(self.app.defaults["global_draw_color"] + '80'), - update=False, - layer=0, - tolerance=None - ) - except TypeError: + if type(geo.geo) == list: + for el in geo.geo: + # Add the new utility shape self.tool_shape.add( - shape=geo.geo, color=(self.app.defaults["global_draw_color"] + '80'), + shape=el, color=(self.app.defaults["global_draw_color"] + '80'), update=False, layer=0, tolerance=None) + else: + # Add the new utility shape + self.tool_shape.add( + shape=geo.geo, color=(self.app.defaults["global_draw_color"] + '80'), + update=False, layer=0, tolerance=None) - self.tool_shape.redraw() + self.tool_shape.redraw() def plot_all(self): """ @@ -1963,6 +1809,28 @@ class FlatCAMGrbEditor(QtCore.QObject): self.shapes.redraw() + def plot_shape(self, geometry=None, color='black', linewidth=1): + """ + Plots a geometric object or list of objects without rendering. Plotted objects + are returned as a list. This allows for efficient/animated rendering. + + :param geometry: Geometry to be plotted (Any Shapely.geom kind or list of such) + :param color: Shape color + :param linewidth: Width of lines in # of pixels. + :return: List of plotted elements. + """ + # plot_elements = [] + + if geometry is None: + geometry = self.active_tool.geometry + + try: + self.shapes.add(shape=geometry.geo, color=color, face_color=color, layer=0) + except AttributeError: + if type(geometry) == Point: + return + self.shapes.add(shape=geometry, color=color, face_color=color+'AF', layer=0) + def start_delayed_plot(self, check_period): # self.plot_thread = threading.Thread(target=lambda: self.check_plot_finished(check_period)) # self.plot_thread.start() @@ -1987,54 +1855,6 @@ class FlatCAMGrbEditor(QtCore.QObject): except Exception: traceback.print_exc() - # def stop_delayed_plot(self): - # self.plot_thread.exit() - # # self.plot_thread.join() - - # def check_plot_finished(self, delay): - # """ - # Using Alfe's answer from here: - # https://stackoverflow.com/questions/474528/what-is-the-best-way-to-repeatedly-execute-a-function-every-x-seconds-in-python - # - # :param delay: period of checking if project file size is more than zero; in seconds - # :param filename: the name of the project file to be checked for size more than zero - # :return: - # """ - # next_time = time.time() + delay - # while True: - # time.sleep(max(0, next_time - time.time())) - # try: - # if self.app.collection.has_plot_promises() is False: - # self.plot_all() - # break - # except Exception: - # traceback.print_exc() - # - # # skip tasks if we are behind schedule: - # next_time += (time.time() - next_time) // delay * delay + delay - - def plot_shape(self, geometry=None, color='black', linewidth=1): - """ - Plots a geometric object or list of objects without rendering. Plotted objects - are returned as a list. This allows for efficient/animated rendering. - - :param geometry: Geometry to be plotted (Any Shapely.geom kind or list of such) - :param color: Shape color - :param linewidth: Width of lines in # of pixels. - :return: List of plotted elements. - """ - # plot_elements = [] - - if geometry is None: - geometry = self.active_tool.geometry - - try: - self.shapes.add(shape=geometry.geo, color=color, face_color=color, layer=0) - except AttributeError: - if type(geometry) == Point: - return - self.shapes.add(shape=geometry, color=color, face_color=color+'AF', layer=0) - def on_shape_complete(self): self.app.log.debug("on_shape_complete()") @@ -2065,7 +1885,7 @@ class FlatCAMGrbEditor(QtCore.QObject): self.selected = [] self.build_ui() - self.app.inform.emit(_("[success] Done. Drill(s) deleted.")) + self.app.inform.emit(_("[success] Done. Apertures deleted.")) def delete_shape(self, shape): self.is_modified = True @@ -2079,14 +1899,8 @@ class FlatCAMGrbEditor(QtCore.QObject): # self.storage_dict[storage].remove(shape) # except: # pass - if shape in self.storage_dict[storage].get_objects(): - self.storage_dict[storage].remove(shape) - # a hack to make the tool_table display less drills per diameter - # self.points_edit it's only useful first time when we load the data into the storage - # but is still used as referecen when building tool_table in self.build_ui() - # the number of drills displayed in column 2 is just a len(self.points_edit) therefore - # deleting self.points_edit elements (doesn't matter who but just the number) solved the display issue. - del self.points_edit[storage][0] + if shape in self.storage_dict[storage]['solid_geometry']: + self.storage_dict[storage]['solid_geometry'].remove(shape) if shape in self.selected: self.selected.remove(shape) # TODO: Check performance @@ -2127,34 +1941,11 @@ class FlatCAMGrbEditor(QtCore.QObject): if shape in self.selected: self.selected.remove(shape) - def on_array_type_combo(self): - if self.array_type_combo.currentIndex() == 0: - self.array_circular_frame.hide() - self.array_linear_frame.show() - else: - self.delete_utility_geometry() - self.array_circular_frame.show() - self.array_linear_frame.hide() - self.app.inform.emit(_("Click on the circular array Center position")) - - def on_linear_angle_radio(self): - val = self.drill_axis_radio.get_value() - if val == 'A': - self.linear_angle_spinner.show() - self.linear_angle_label.show() - else: - self.linear_angle_spinner.hide() - self.linear_angle_label.hide() - - def exc_resize_drills(self): - self.select_tool('resize') - return - - def exc_copy_drills(self): + def on_copy_button(self): self.select_tool('copy') return - def exc_move_drills(self): + def on_move_button(self): self.select_tool('move') return @@ -2177,14 +1968,17 @@ class FlatCAMGrbEditor(QtCore.QObject): # I populated the combobox such that the index coincide with the join styles value (whcih is really an INT) join_style = self.buffer_corner_cb.currentIndex() + 1 - def buffer_recursion(geom): + def buffer_recursion(geom, selection): if type(geom) == list or type(geom) is MultiPolygon: geoms = list() for local_geom in geom: - geoms.append(buffer_recursion(local_geom)) + geoms.append(buffer_recursion(local_geom, selection=selection)) return geoms else: - return DrawToolShape(geom.geo.buffer(buff_value, join_style=join_style)) + if geom in selection: + return DrawToolShape(geom.geo.buffer(buff_value, join_style=join_style)) + else: + return geom if not self.apertures_table.selectedItems(): self.app.inform.emit(_( @@ -2196,13 +1990,12 @@ class FlatCAMGrbEditor(QtCore.QObject): try: apid = self.apertures_table.item(x.row(), 1).text() - temp_storage = deepcopy(buffer_recursion(self.storage_dict[apid]['solid_geometry'])) + temp_storage = deepcopy(buffer_recursion(self.storage_dict[apid]['solid_geometry'], self.selected)) self.storage_dict[apid]['solid_geometry'] = [] self.storage_dict[apid]['solid_geometry'] = temp_storage except Exception as e: log.debug("FlatCAMGrbEditor.buffer() --> %s" % str(e)) - self.plot_all() self.app.inform.emit(_("[success] Done. Buffer Tool completed.")) @@ -2222,14 +2015,17 @@ class FlatCAMGrbEditor(QtCore.QObject): "Add it and retry.")) return - def scale_recursion(geom): + def scale_recursion(geom, selection): if type(geom) == list or type(geom) is MultiPolygon: geoms = list() for local_geom in geom: - geoms.append(scale_recursion(local_geom)) + geoms.append(scale_recursion(local_geom, selection=selection)) return geoms else: - return DrawToolShape(affinity.scale(geom.geo, scale_factor, scale_factor, origin='center')) + if geom in selection: + return DrawToolShape(affinity.scale(geom.geo, scale_factor, scale_factor, origin='center')) + else: + return geom if not self.apertures_table.selectedItems(): self.app.inform.emit(_( @@ -2241,7 +2037,7 @@ class FlatCAMGrbEditor(QtCore.QObject): try: apid = self.apertures_table.item(x.row(), 1).text() - temp_storage = deepcopy(scale_recursion(self.storage_dict[apid]['solid_geometry'])) + temp_storage = deepcopy(scale_recursion(self.storage_dict[apid]['solid_geometry'], self.selected)) self.storage_dict[apid]['solid_geometry'] = [] self.storage_dict[apid]['solid_geometry'] = temp_storage diff --git a/flatcamGUI/FlatCAMGUI.py b/flatcamGUI/FlatCAMGUI.py index 2551d18b..46ebc371 100644 --- a/flatcamGUI/FlatCAMGUI.py +++ b/flatcamGUI/FlatCAMGUI.py @@ -672,6 +672,8 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.grb_select_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/pointer32.png'), _("Select")) self.aperture_buffer_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/buffer16-2.png'), _('Buffer')) self.aperture_scale_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/scale32.png'), _('Scale')) + self.aperture_copy_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/copy32.png'), _("Copy")) + self.aperture_move_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/move32.png'), _("Move")) ### Snap Toolbar ### @@ -2409,7 +2411,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.app.grb_editor.launched_from_shortcuts = True if self.app.grb_editor.selected: self.app.grb_editor.delete_selected() - self.app.grb_editor.replot() + self.app.grb_editor.plot_all() else: self.app.inform.emit(_("[WARNING_NOTCL] Cancelled. Nothing selected to delete.")) return @@ -2417,7 +2419,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow): # Delete aperture in apertures table if delete key event comes from the Selected Tab if key == QtCore.Qt.Key_Delete: self.app.grb_editor.launched_from_shortcuts = True - self.app.grb_editor.on_tool_delete() + self.app.grb_editor.on_aperture_delete() return if key == QtCore.Qt.Key_Minus or key == '-': @@ -2461,7 +2463,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.app.grb_editor.launched_from_shortcuts = True if self.app.grb_editor.selected: self.app.inform.emit(_("Click on target point.")) - self.app.ui.copy_aperture_btn.setChecked(True) + self.app.ui.aperture_copy_btn.setChecked(True) self.app.grb_editor.on_tool_select('aperture_copy') self.app.grb_editor.active_tool.set_origin( (self.app.grb_editor.snap_x, self.app.grb_editor.snap_y)) @@ -2481,6 +2483,12 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.app.grb_editor.select_tool('aperture_add') return + # Scale Tool + if key == QtCore.Qt.Key_B or key == 'B': + self.app.grb_editor.launched_from_shortcuts = True + self.app.grb_editor.select_tool('aperture_buffer') + return + # Grid Snap if key == QtCore.Qt.Key_G or key == 'G': self.app.grb_editor.launched_from_shortcuts = True @@ -2507,18 +2515,18 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.app.grb_editor.launched_from_shortcuts = True if self.app.grb_editor.selected: self.app.inform.emit(_("Click on target point.")) - self.app.ui.move_aperture_btn.setChecked(True) - self.app.exc_editor.on_tool_select('aperture_move') + self.app.ui.aperture_move_btn.setChecked(True) + self.app.grb_editor.on_tool_select('aperture_move') self.app.grb_editor.active_tool.set_origin( (self.app.grb_editor.snap_x, self.app.grb_editor.snap_y)) else: self.app.inform.emit(_("[WARNING_NOTCL] Cancelled. Nothing selected to move.")) return - # Resize Tool - if key == QtCore.Qt.Key_R or key == 'R': + # Scale Tool + if key == QtCore.Qt.Key_S or key == 'S': self.app.grb_editor.launched_from_shortcuts = True - self.app.grb_editor.select_tool('drill_resize') + self.app.grb_editor.select_tool('aperture_scale') return # Add Track diff --git a/flatcamGUI/PlotCanvas.py b/flatcamGUI/PlotCanvas.py index 374a1d65..e5ffb775 100644 --- a/flatcamGUI/PlotCanvas.py +++ b/flatcamGUI/PlotCanvas.py @@ -148,8 +148,11 @@ class PlotCanvas(QtCore.QObject): def vis_connect(self, event_name, callback): return getattr(self.vispy_canvas.events, event_name).connect(callback) - def vis_disconnect(self, event_name, callback): - getattr(self.vispy_canvas.events, event_name).disconnect(callback) + def vis_disconnect(self, event_name, callback=None): + if callback is None: + getattr(self.vispy_canvas.events, event_name).disconnect() + else: + getattr(self.vispy_canvas.events, event_name).disconnect(callback) def zoom(self, factor, center=None): """ diff --git a/flatcamTools/ToolMeasurement.py b/flatcamTools/ToolMeasurement.py index 46698b37..0f31b923 100644 --- a/flatcamTools/ToolMeasurement.py +++ b/flatcamTools/ToolMeasurement.py @@ -28,6 +28,8 @@ class Measurement(FlatCAMTool): def __init__(self, app): FlatCAMTool.__init__(self, app) + self.app = app + self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower() ## Title @@ -38,11 +40,11 @@ class Measurement(FlatCAMTool): form_layout = QtWidgets.QFormLayout() self.layout.addLayout(form_layout) - form_layout_child_1 = QtWidgets.QFormLayout() - form_layout_child_1_1 = QtWidgets.QFormLayout() - form_layout_child_1_2 = QtWidgets.QFormLayout() - form_layout_child_2 = QtWidgets.QFormLayout() - form_layout_child_3 = QtWidgets.QFormLayout() + + self.units_label = QtWidgets.QLabel(_("Units:")) + self.units_label.setToolTip(_("Those are the units in which the distance is measured.")) + self.units_value = QtWidgets.QLabel("%s" % str({'mm': "METRIC (mm)", 'in': "INCH (in)"}[self.units])) + self.units_value.setDisabled(True) self.start_label = QtWidgets.QLabel("%s %s:" % (_('Start'), _('Coords'))) self.start_label.setToolTip(_("This is measuring Start point coordinates.")) @@ -59,84 +61,38 @@ class Measurement(FlatCAMTool): self.total_distance_label = QtWidgets.QLabel("%s:" % _('DISTANCE')) self.total_distance_label.setToolTip(_("This is the point to point Euclidian distance.")) - self.units_entry_1 = FCEntry() - self.units_entry_1.setToolTip(_("Those are the units in which the distance is measured.")) - self.units_entry_1.setDisabled(True) - self.units_entry_1.setFocusPolicy(QtCore.Qt.NoFocus) - self.units_entry_1.setFrame(False) - self.units_entry_1.setFixedWidth(30) - - self.units_entry_2 = FCEntry() - self.units_entry_2.setToolTip(_("Those are the units in which the distance is measured.")) - self.units_entry_2.setDisabled(True) - self.units_entry_2.setFocusPolicy(QtCore.Qt.NoFocus) - self.units_entry_2.setFrame(False) - self.units_entry_2.setFixedWidth(30) - - self.units_entry_3 = FCEntry() - self.units_entry_3.setToolTip(_("Those are the units in which the distance is measured.")) - self.units_entry_3.setDisabled(True) - self.units_entry_3.setFocusPolicy(QtCore.Qt.NoFocus) - self.units_entry_3.setFrame(False) - self.units_entry_3.setFixedWidth(30) - - self.units_entry_4 = FCEntry() - self.units_entry_4.setToolTip(_("Those are the units in which the distance is measured.")) - self.units_entry_4.setDisabled(True) - self.units_entry_4.setFocusPolicy(QtCore.Qt.NoFocus) - self.units_entry_4.setFrame(False) - self.units_entry_4.setFixedWidth(30) - - self.units_entry_5 = FCEntry() - self.units_entry_5.setToolTip(_("Those are the units in which the distance is measured.")) - self.units_entry_5.setDisabled(True) - self.units_entry_5.setFocusPolicy(QtCore.Qt.NoFocus) - self.units_entry_5.setFrame(False) - self.units_entry_5.setFixedWidth(30) - self.start_entry = FCEntry() self.start_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) self.start_entry.setToolTip(_("This is measuring Start point coordinates.")) - self.start_entry.setFixedWidth(100) self.stop_entry = FCEntry() self.stop_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) self.stop_entry.setToolTip(_("This is the measuring Stop point coordinates.")) - self.stop_entry.setFixedWidth(100) self.distance_x_entry = FCEntry() self.distance_x_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) self.distance_x_entry.setToolTip(_("This is the distance measured over the X axis.")) - self.distance_x_entry.setFixedWidth(100) self.distance_y_entry = FCEntry() self.distance_y_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) self.distance_y_entry.setToolTip(_("This is the distance measured over the Y axis.")) - self.distance_y_entry.setFixedWidth(100) self.total_distance_entry = FCEntry() self.total_distance_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) self.total_distance_entry.setToolTip(_("This is the point to point Euclidian distance.")) - self.total_distance_entry.setFixedWidth(100) self.measure_btn = QtWidgets.QPushButton(_("Measure")) - self.measure_btn.setFixedWidth(70) + # self.measure_btn.setFixedWidth(70) self.layout.addWidget(self.measure_btn) - - form_layout_child_1.addRow(self.start_entry, self.units_entry_1) - form_layout_child_1_1.addRow(self.stop_entry, self.units_entry_2) - form_layout_child_1_2.addRow(self.distance_x_entry, self.units_entry_3) - form_layout_child_2.addRow(self.distance_y_entry, self.units_entry_4) - form_layout_child_3.addRow(self.total_distance_entry, self.units_entry_5) - - form_layout.addRow(self.start_label, form_layout_child_1) - form_layout.addRow(self.stop_label, form_layout_child_1_1) - form_layout.addRow(self.distance_x_label, form_layout_child_1_2) - form_layout.addRow(self.distance_y_label, form_layout_child_2) - form_layout.addRow(self.total_distance_label, form_layout_child_3) + form_layout.addRow(self.units_label, self.units_value) + form_layout.addRow(self.start_label, self.start_entry) + form_layout.addRow(self.stop_label, self.stop_entry) + form_layout.addRow(self.distance_x_label, self.distance_x_entry) + form_layout.addRow(self.distance_y_label, self.distance_y_entry) + form_layout.addRow(self.total_distance_label, self.total_distance_entry) # initial view of the layout self.start_entry.set_value('(0, 0)') @@ -144,12 +100,6 @@ class Measurement(FlatCAMTool): self.distance_x_entry.set_value('0') self.distance_y_entry.set_value('0') self.total_distance_entry.set_value('0') - self.units_entry_1.set_value(str(self.units)) - self.units_entry_2.set_value(str(self.units)) - self.units_entry_3.set_value(str(self.units)) - self.units_entry_4.set_value(str(self.units)) - self.units_entry_5.set_value(str(self.units)) - self.layout.addStretch() @@ -160,12 +110,12 @@ class Measurement(FlatCAMTool): # the default state is disabled for the Move command # self.setVisible(False) - self.active = False + self.active = 0 # VisPy visuals self.sel_shapes = ShapeCollection(parent=self.app.plotcanvas.vispy_canvas.view.scene, layers=1) - self.measure_btn.clicked.connect(self.toggle_f) + self.measure_btn.clicked.connect(lambda: self.on_measure(activate=True)) def run(self, toggle=False): self.app.report_usage("ToolMeasurement()") @@ -173,14 +123,13 @@ class Measurement(FlatCAMTool): if self.app.tool_tab_locked is True: return + self.app.ui.notebook.setTabText(2, _("Meas. Tool")) + # if the splitter is hidden, display it, else hide it but only if the current widget is the same if self.app.ui.splitter.sizes()[0] == 0: self.app.ui.splitter.setSizes([1, 1]) - self.toggle_f() - - self.set_tool_ui() - self.app.ui.notebook.setTabText(2, _("Meas. Tool")) + self.on_measure(activate=True) def install(self, icon=None, separator=None, **kwargs): FlatCAMTool.install(self, icon, separator, shortcut='CTRL+M', **kwargs) @@ -195,101 +144,92 @@ class Measurement(FlatCAMTool): # Switch notebook to tool page self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab) self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower() - self.show() - def toggle_f(self): - # the self.active var is doing the 'toggle' - if self.active is True: + self.app.command_active = "Measurement" + + # initial view of the layout + self.start_entry.set_value('(0, 0)') + self.stop_entry.set_value('(0, 0)') + + self.distance_x_entry.set_value('0') + self.distance_y_entry.set_value('0') + self.total_distance_entry.set_value('0') + + def activate(self): + # we disconnect the mouse/key handlers from wherever the measurement tool was called + self.app.plotcanvas.vis_disconnect('key_press') + self.app.plotcanvas.vis_disconnect('mouse_double_click') + self.app.plotcanvas.vis_disconnect('mouse_move') + self.app.plotcanvas.vis_disconnect('mouse_press') + self.app.plotcanvas.vis_disconnect('mouse_release') + self.app.plotcanvas.vis_disconnect('key_release') + + # we can safely connect the app mouse events to the measurement tool + self.app.plotcanvas.vis_connect('mouse_move', self.on_mouse_move_meas) + self.app.plotcanvas.vis_connect('mouse_release', self.on_mouse_click) + self.app.plotcanvas.vis_connect('key_release', self.on_key_release_meas) + + self.set_tool_ui() + + def deactivate(self): + # disconnect the mouse/key events from functions of measurement tool + self.app.plotcanvas.vis_disconnect('mouse_move') + self.app.plotcanvas.vis_disconnect('mouse_press') + self.app.plotcanvas.vis_disconnect('key_release') + + # reconnect the mouse/key events to the functions from where the tool was called + self.app.plotcanvas.vis_connect('key_press', self.app.ui.keyPressEvent) + self.app.plotcanvas.vis_connect('mouse_double_click', self.app.on_double_click_over_plot) + + if self.app.call_source == 'app': + self.app.plotcanvas.vis_connect('mouse_move', self.app.on_mouse_move_over_plot) + self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot) + self.app.plotcanvas.vis_connect('mouse_release', self.app.on_mouse_click_release_over_plot) + elif self.app.call_source == 'geo_editor': + self.app.geo_editor.canvas.vis_connect('mouse_move', self.app.geo_editor.on_canvas_move) + self.app.geo_editor.canvas.vis_connect('mouse_press', self.app.geo_editor.on_canvas_click) + # self.app.geo_editor.canvas.vis_connect('key_press', self.app.geo_editor.on_canvas_key) + self.app.geo_editor.canvas.vis_connect('mouse_release', self.app.geo_editor.on_canvas_click_release) + elif self.app.call_source == 'exc_editor': + self.app.exc_editor.canvas.vis_connect('mouse_move', self.app.exc_editor.on_canvas_move) + self.app.exc_editor.canvas.vis_connect('mouse_press', self.app.exc_editor.on_canvas_click) + # self.app.exc_editor.canvas.vis_connect('key_press', self.app.exc_editor.on_canvas_key) + self.app.exc_editor.canvas.vis_connect('mouse_release', self.app.exc_editor.on_canvas_click_release) + elif self.app.call_source == 'grb_editor': + self.app.grb_editor.canvas.vis_connect('mouse_move', self.app.grb_editor.on_canvas_move) + self.app.grb_editor.canvas.vis_connect('mouse_press', self.app.grb_editor.on_canvas_click) + # self.app.grb_editor.canvas.vis_connect('key_press', self.app.grb_editor.on_canvas_key) + self.app.grb_editor.canvas.vis_connect('mouse_release', self.app.grb_editor.on_canvas_click_release) + + self.app.ui.notebook.setTabText(2, _("Tools")) + + def on_measure(self, signal=None, activate=None): + if activate is False or activate is None: # DISABLE the Measuring TOOL - self.active = False - - # disconnect the mouse/key events from functions of measurement tool - self.app.plotcanvas.vis_disconnect('mouse_move', self.on_mouse_move_meas) - self.app.plotcanvas.vis_disconnect('mouse_press', self.on_click_meas) - - # reconnect the mouse/key events to the functions from where the tool was called - if self.app.call_source == 'app': - self.app.plotcanvas.vis_connect('mouse_move', self.app.on_mouse_move_over_plot) - self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot) - self.app.plotcanvas.vis_connect('key_press', self.app.ui.keyPressEvent) - self.app.plotcanvas.vis_connect('mouse_release', self.app.on_mouse_click_release_over_plot) - elif self.app.call_source == 'geo_editor': - self.app.geo_editor.canvas.vis_connect('mouse_move', self.app.geo_editor.on_canvas_move) - self.app.geo_editor.canvas.vis_connect('mouse_press', self.app.geo_editor.on_canvas_click) - self.app.geo_editor.canvas.vis_connect('key_press', self.app.geo_editor.on_canvas_key) - self.app.geo_editor.canvas.vis_connect('mouse_release', self.app.geo_editor.on_canvas_click_release) - elif self.app.call_source == 'exc_editor': - self.app.exc_editor.canvas.vis_connect('mouse_move', self.app.exc_editor.on_canvas_move) - self.app.exc_editor.canvas.vis_connect('mouse_press', self.app.exc_editor.on_canvas_click) - self.app.exc_editor.canvas.vis_connect('key_press', self.app.exc_editor.on_canvas_key) - self.app.exc_editor.canvas.vis_connect('mouse_release', self.app.exc_editor.on_canvas_click_release) - # elif self.app.call_source == 'grb_editor': - # self.app.grb_editor.canvas.vis_connect('mouse_move', self.app.grb_editor.on_canvas_move) - # self.app.grb_editor.canvas.vis_connect('mouse_press', self.app.grb_editor.on_canvas_click) - # self.app.grb_editor.canvas.vis_connect('key_press', self.app.grb_editor.on_canvas_key) - # self.app.grb_editor.canvas.vis_connect('mouse_release', self.app.grb_editor.on_canvas_click_release) + self.deactivate() self.app.call_source = 'measurement' - self.clicked_meas = 0 self.app.command_active = None + # delete the measuring line self.delete_shape() - return - else: + + log.debug("Measurement Tool --> exit tool") + elif activate is True: # ENABLE the Measuring TOOL - self.active = True - self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower() - - # we disconnect the mouse/key handlers from wherever the measurement tool was called - if self.app.call_source == 'app': - self.app.plotcanvas.vis_disconnect('mouse_move', self.app.on_mouse_move_over_plot) - self.app.plotcanvas.vis_disconnect('mouse_press', self.app.on_mouse_click_over_plot) - self.app.plotcanvas.vis_disconnect('key_press', self.app.ui.keyPressEvent) - self.app.plotcanvas.vis_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot) - elif self.app.call_source == 'geo_editor': - self.app.geo_editor.canvas.vis_disconnect('mouse_move', self.app.geo_editor.on_canvas_move) - self.app.geo_editor.canvas.vis_disconnect('mouse_press', self.app.geo_editor.on_canvas_click) - self.app.geo_editor.canvas.vis_disconnect('key_press', self.app.geo_editor.on_canvas_key) - self.app.geo_editor.canvas.vis_disconnect('mouse_release', self.app.geo_editor.on_canvas_click_release) - elif self.app.call_source == 'exc_editor': - self.app.exc_editor.canvas.vis_disconnect('mouse_move', self.app.exc_editor.on_canvas_move) - self.app.exc_editor.canvas.vis_disconnect('mouse_press', self.app.exc_editor.on_canvas_click) - self.app.exc_editor.canvas.vis_disconnect('key_press', self.app.exc_editor.on_canvas_key) - self.app.exc_editor.canvas.vis_disconnect('mouse_release', self.app.exc_editor.on_canvas_click_release) - # elif self.app.call_source == 'grb_editor': - # self.app.grb_editor.canvas.vis_disconnect('mouse_move', self.app.grb_editor.on_canvas_move) - # self.app.grb_editor.canvas.vis_disconnect('mouse_press', self.app.grb_editor.on_canvas_click) - # self.app.grb_editor.canvas.vis_disconnect('key_press', self.app.grb_editor.on_canvas_key) - # self.app.grb_editor.canvas.vis_disconnect('mouse_release', self.app.grb_editor.on_canvas_click_release) - - # we can safely connect the app mouse events to the measurement tool - self.app.plotcanvas.vis_connect('mouse_move', self.on_mouse_move_meas) - self.app.plotcanvas.vis_connect('mouse_release', self.on_click_meas) - self.app.plotcanvas.vis_connect('key_release', self.on_key_release_meas) - - self.app.command_active = "Measurement" - - # initial view of the layout - self.start_entry.set_value('(0, 0)') - self.stop_entry.set_value('(0, 0)') - - self.distance_x_entry.set_value('0') - self.distance_y_entry.set_value('0') - self.total_distance_entry.set_value('0') - - self.units_entry_1.set_value(str(self.units)) - self.units_entry_2.set_value(str(self.units)) - self.units_entry_3.set_value(str(self.units)) - self.units_entry_4.set_value(str(self.units)) - self.units_entry_5.set_value(str(self.units)) + self.clicked_meas = 0 self.app.inform.emit(_("MEASURING: Click on the Start point ...")) + self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower() + + self.activate() + log.debug("Measurement Tool --> tool initialized") def on_key_release_meas(self, event): if event.key == 'escape': # abort the measurement action - self.toggle_f() - self.app.inform.emit("") + self.on_measure(activate=False) + self.app.inform.emit(_("Measurement Tool exit...")) return if event.key == 'G': @@ -297,28 +237,29 @@ class Measurement(FlatCAMTool): self.app.ui.grid_snap_btn.trigger() return - def on_click_meas(self, event): + def on_mouse_click(self, event): # mouse click releases will be accepted only if the left button is clicked # this is necessary because right mouse click or middle mouse click # are used for panning on the canvas if event.button == 1: + pos_canvas = self.app.plotcanvas.vispy_canvas.translate_coords(event.pos) + if self.clicked_meas == 0: - pos_canvas = self.app.plotcanvas.vispy_canvas.translate_coords(event.pos) + self.clicked_meas = 1 # if GRID is active we need to get the snapped positions if self.app.grid_status() == True: pos = self.app.geo_editor.snap(pos_canvas[0], pos_canvas[1]) else: pos = pos_canvas[0], pos_canvas[1] + self.point1 = pos self.start_entry.set_value("(%.4f, %.4f)" % pos) self.app.inform.emit(_("MEASURING: Click on the Destination point ...")) - if self.clicked_meas == 1: + else: try: - pos_canvas = self.app.plotcanvas.vispy_canvas.translate_coords(event.pos) - # delete the selection bounding box self.delete_shape() @@ -341,16 +282,12 @@ class Measurement(FlatCAMTool): self.distance_y_entry.set_value('%.4f' % abs(dy)) self.total_distance_entry.set_value('%.4f' % abs(d)) - self.clicked_meas = 0 - self.toggle_f() + self.on_measure(activate=False) # delete the measuring line self.delete_shape() - return - except TypeError: - pass - - self.clicked_meas = 1 + except TypeError as e: + log.debug("Measurement.on_click_meas() --> %s" % str(e)) def on_mouse_move_meas(self, event): pos_canvas = self.app.plotcanvas.vispy_canvas.translate_coords(event.pos)