diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 80df6cab..5324290e 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -240,6 +240,9 @@ class App(QtCore.QObject): # signal emitted when jumping jump_signal = pyqtSignal(tuple) + # signal emitted when jumping + locate_signal = pyqtSignal(tuple, str) + # close app signal close_app_signal = pyqtSignal() @@ -429,6 +432,7 @@ class App(QtCore.QObject): "global_stats": dict(), "global_tabs_detachable": True, "global_jump_ref": 'abs', + "global_locate_pt": 'bl', "global_tpdf_tmargin": 15.0, "global_tpdf_bmargin": 10.0, "global_tpdf_lmargin": 20.0, @@ -1956,6 +1960,7 @@ class App(QtCore.QObject): self.ui.menueditorigin.triggered.connect(self.on_set_origin) self.ui.menueditjump.triggered.connect(self.on_jump_to) + self.ui.menueditlocate.triggered.connect(lambda: self.on_locate(obj=self.collection.get_active())) self.ui.menuedittoggleunits.triggered.connect(self.on_toggle_units_click) self.ui.menueditselectall.triggered.connect(self.on_selectall) @@ -3241,6 +3246,7 @@ class App(QtCore.QObject): self.ui.distance_min_btn.triggered.connect(lambda: self.distance_min_tool.run(toggle=True)) self.ui.origin_btn.triggered.connect(self.on_set_origin) self.ui.jmp_btn.triggered.connect(self.on_jump_to) + self.ui.locate_btn.triggered.connect(lambda: self.on_locate(obj=self.collection.get_active())) self.ui.shell_btn.triggered.connect(self.on_toggle_shell) self.ui.new_script_btn.triggered.connect(self.on_filenewscript) @@ -3250,6 +3256,9 @@ class App(QtCore.QObject): # Tools Toolbar Signals self.ui.dblsided_btn.triggered.connect(lambda: self.dblsidedtool.run(toggle=True)) self.ui.cal_btn.triggered.connect(lambda: self.cal_exc_tool.run(toggle=True)) + self.ui.align_btn.triggered.connect(lambda: self.align_objects_tool.run(toggle=True)) + self.ui.extract_btn.triggered.connect(lambda: self.edrills_tool.run(toggle=True)) + self.ui.cutout_btn.triggered.connect(lambda: self.cutout_tool.run(toggle=True)) self.ui.ncc_btn.triggered.connect(lambda: self.ncclear_tool.run(toggle=True)) self.ui.paint_btn.triggered.connect(lambda: self.paint_tool.run(toggle=True)) @@ -7288,7 +7297,151 @@ class App(QtCore.QObject): self.jump_signal.emit(location) - units = self.defaults['units'].upper() + if fit_center: + self.plotcanvas.fit_center(loc=location) + + cursor = QtGui.QCursor() + + if self.is_legacy is False: + # I don't know where those differences come from but they are constant for the current + # execution of the application and they are multiples of a value around 0.0263mm. + # In a random way sometimes they are more sometimes they are less + # if units == 'MM': + # cal_factor = 0.0263 + # else: + # cal_factor = 0.0263 / 25.4 + + cal_location = (location[0], location[1]) + + canvas_origin = self.plotcanvas.native.mapToGlobal(QtCore.QPoint(0, 0)) + jump_loc = self.plotcanvas.translate_coords_2((cal_location[0], cal_location[1])) + + j_pos = ( + int(canvas_origin.x() + round(jump_loc[0])), + int(canvas_origin.y() + round(jump_loc[1])) + ) + cursor.setPos(j_pos[0], j_pos[1]) + else: + # find the canvas origin which is in the top left corner + canvas_origin = self.plotcanvas.native.mapToGlobal(QtCore.QPoint(0, 0)) + # determine the coordinates for the lowest left point of the canvas + x0, y0 = canvas_origin.x(), canvas_origin.y() + self.ui.right_layout.geometry().height() + + # transform the given location from data coordinates to display coordinates. THe display coordinates are + # in pixels where the origin 0,0 is in the lowest left point of the display window (in our case is the + # canvas) and the point (width, height) is in the top-right location + loc = self.plotcanvas.axes.transData.transform_point(location) + j_pos = ( + int(x0 + loc[0]), + int(y0 - loc[1]) + ) + cursor.setPos(j_pos[0], j_pos[1]) + self.plotcanvas.mouse = [location[0], location[1]] + if self.defaults["global_cursor_color_enabled"] is True: + self.plotcanvas.draw_cursor(x_pos=location[0], y_pos=location[1], color=self.cursor_color_3D) + else: + self.plotcanvas.draw_cursor(x_pos=location[0], y_pos=location[1]) + + if self.grid_status(): + # Update cursor + self.app_cursor.set_data(np.asarray([(location[0], location[1])]), + symbol='++', edge_color=self.cursor_color_3D, + edge_width=self.defaults["global_cursor_width"], + size=self.defaults["global_cursor_size"]) + + # Set the position label + self.ui.position_label.setText("    X: %.4f   " + "Y: %.4f" % (location[0], location[1])) + # Set the relative position label + dx = location[0] - float(self.rel_point1[0]) + dy = location[1] - float(self.rel_point1[1]) + self.ui.rel_position_label.setText("Dx: %.4f   Dy: " + "%.4f    " % (dx, dy)) + + self.inform.emit('[success] %s' % _("Done.")) + return location + + def on_locate(self, obj, fit_center=True): + """ + Jump to one of the corners (or center) of an object by setting the mouse cursor location + :return: + + """ + self.report_usage("on_locate()") + + if obj is None: + self.inform.emit('[WARNING_NOTCL] %s' % _("There is no object selected...")) + return 'fail' + + class DialogBoxChoice(QtWidgets.QDialog): + def __init__(self, title=None, icon=None, choice='bl'): + """ + + :param title: string with the window title + """ + super(DialogBoxChoice, self).__init__() + + self.ok = False + + self.setWindowIcon(icon) + self.setWindowTitle(str(title)) + + self.form = QtWidgets.QFormLayout(self) + + self.ref_radio = RadioSet([ + {"label": _("Bottom-Left"), "value": "bl"}, + {"label": _("Top-Left"), "value": "tl"}, + {"label": _("Bottom-Right"), "value": "br"}, + {"label": _("Top-Right"), "value": "tr"}, + {"label": _("Center"), "value": "c"} + ], orientation='vertical', stretch=False) + self.ref_radio.set_value(choice) + self.form.addRow(self.ref_radio) + + self.button_box = QtWidgets.QDialogButtonBox( + QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel, + Qt.Horizontal, parent=self) + self.form.addRow(self.button_box) + + self.button_box.accepted.connect(self.accept) + self.button_box.rejected.connect(self.reject) + + if self.exec_() == QtWidgets.QDialog.Accepted: + self.ok = True + self.location_point = self.ref_radio.get_value() + else: + self.ok = False + self.location_point = None + + dia_box = DialogBoxChoice(title=_("Locate ..."), + icon=QtGui.QIcon(self.resource_location + '/locate16.png'), + choice=self.defaults['global_locate_pt']) + + if dia_box.ok is True: + try: + location_point = dia_box.location_point + self.defaults['global_locate_pt'] = dia_box.location_point + except Exception: + return + else: + return + + loc_b = obj.bounds() + if location_point == 'bl': + location = (loc_b[0], loc_b[1]) + elif location_point == 'tl': + location = (loc_b[0], loc_b[3]) + elif location_point == 'br': + location = (loc_b[2], loc_b[1]) + elif location_point == 'tr': + location = (loc_b[2], loc_b[3]) + else: + # center + cx = loc_b[0] + ((loc_b[2] - loc_b[0]) / 2) + cy = loc_b[1] + ((loc_b[3] - loc_b[1]) / 2) + location = (cx, cy) + + self.locate_signal.emit(location, location_point) if fit_center: self.plotcanvas.fit_center(loc=location) diff --git a/README.md b/README.md index 5e49cd87..2c446e8a 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,11 @@ CAD program, and create G-Code for Isolation routing. ================================================= +15.01.2020 + +- added key shortcuts and toolbar icons for the new tools: Align Object Tool (ALT+A) and Extract Drills (ALT+I) +- added new functionality (key shortcut SHIFT+J) to locate the corners of the bounding box (and center) in a selected object + 14.01.2020 - in Extract Drill Tool added a new method of drills extraction. The methods are: fixed diameter, fixed annular ring and proportional diff --git a/flatcamGUI/FlatCAMGUI.py b/flatcamGUI/FlatCAMGUI.py index 111a9895..86974e3f 100644 --- a/flatcamGUI/FlatCAMGUI.py +++ b/flatcamGUI/FlatCAMGUI.py @@ -373,6 +373,8 @@ class FlatCAMGUI(QtWidgets.QMainWindow): QtGui.QIcon(self.app.resource_location + '/origin16.png'), _('Se&t Origin\tO')) self.menueditjump = self.menuedit.addAction( QtGui.QIcon(self.app.resource_location + '/jump_to16.png'), _('Jump to Location\tJ')) + self.menueditlocate = self.menuedit.addAction( + QtGui.QIcon(self.app.resource_location + '/locate16.png'), _('Locate in Object\tSHIFT+J')) # Separator self.menuedit.addSeparator() @@ -825,6 +827,8 @@ class FlatCAMGUI(QtWidgets.QMainWindow): QtGui.QIcon(self.app.resource_location + '/origin32.png'), _('Set Origin')) self.jmp_btn = self.toolbargeo.addAction( QtGui.QIcon(self.app.resource_location + '/jump_to16.png'), _('Jump to Location')) + self.locate_btn = self.toolbargeo.addAction( + QtGui.QIcon(self.app.resource_location + '/locate32.png'), _('Locate in Object')) # ######################################################################## # ########################## View Toolbar# ############################### @@ -859,6 +863,11 @@ class FlatCAMGUI(QtWidgets.QMainWindow): # ######################################################################## self.dblsided_btn = self.toolbartools.addAction( QtGui.QIcon(self.app.resource_location + '/doubleside32.png'), _("2Sided Tool")) + self.align_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/align32.png'), _("Align Objects Tool")) + self.extract_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/extract_drill32.png'), _("Extract Drills Tool")) + self.cutout_btn = self.toolbartools.addAction( QtGui.QIcon(self.app.resource_location + '/cut16_bis.png'), _("Cutout Tool")) self.ncc_btn = self.toolbartools.addAction( @@ -1474,6 +1483,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow): SHIFT+G  %s + + SHIFT+J +  %s + SHIFT+M  %s @@ -1506,6 +1519,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow):     + + ALT+A +  %s + ALT+C  %s @@ -1518,6 +1535,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow): ALT+E  %s + + ALT+I +  %s + ALT+J  %s @@ -1637,11 +1658,13 @@ class FlatCAMGUI(QtWidgets.QMainWindow): # SHIFT section _("Copy Obj_Name"), - _("Toggle Code Editor"), _("Toggle the axis"), _("Distance Minimum Tool"), _("Open Preferences Window"), + _("Toggle Code Editor"), _("Toggle the axis"), _("Locate in Object"), _("Distance Minimum Tool"), + _("Open Preferences Window"), _("Rotate by 90 degree CCW"), _("Run a Script"), _("Toggle the workspace"), _("Skew on X axis"), _("Skew on Y axis"), # ALT section - _("Calculators Tool"), _("2-Sided PCB Tool"), _("Transformations Tool"), _("Fiducials Tool"), + _("Align Objects Tool"), _("Calculators Tool"), _("2-Sided PCB Tool"), _("Transformations Tool"), + _("Extract Drills Tool"), _("Fiducials Tool"), _("Solder Paste Dispensing Tool"), _("Film PCB Tool"), _("Non-Copper Clearing Tool"), _("Optimal Tool"), _("Paint Area Tool"), _("QRCode Tool"), _("Rules Check Tool"), @@ -2457,8 +2480,12 @@ class FlatCAMGUI(QtWidgets.QMainWindow): QtGui.QIcon(self.app.resource_location + '/origin32.png'), _('Set Origin')) self.jmp_btn = self.toolbargeo.addAction( QtGui.QIcon(self.app.resource_location + '/jump_to16.png'), _('Jump to Location')) + self.locate_btn = self.toolbargeo.addAction( + QtGui.QIcon(self.app.resource_location + '/locate32.png'), _('Locate in Object')) - # ## View Toolbar # ## + # ######################################################################## + # ########################## View Toolbar# ############################### + # ######################################################################## self.replot_btn = self.toolbarview.addAction( QtGui.QIcon(self.app.resource_location + '/replot32.png'), _("&Replot")) self.clear_plot_btn = self.toolbarview.addAction( @@ -2470,9 +2497,9 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.zoom_fit_btn = self.toolbarview.addAction( QtGui.QIcon(self.app.resource_location + '/zoom_fit32.png'), _("Zoom Fit")) - # self.toolbarview.setVisible(False) - - # ## Shell Toolbar # ## + # ######################################################################## + # ########################## Shell Toolbar# ############################## + # ######################################################################## self.shell_btn = self.toolbarshell.addAction( QtGui.QIcon(self.app.resource_location + '/shell32.png'), _("&Command Line")) self.new_script_btn = self.toolbarshell.addAction( @@ -2485,6 +2512,11 @@ class FlatCAMGUI(QtWidgets.QMainWindow): # ## Tools Toolbar # ## self.dblsided_btn = self.toolbartools.addAction( QtGui.QIcon(self.app.resource_location + '/doubleside32.png'), _("2Sided Tool")) + self.align_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/align32.png'), _("Align Objects Tool")) + self.extract_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/extract_drill32.png'), _("Extract Drills Tool")) + self.cutout_btn = self.toolbartools.addAction( QtGui.QIcon(self.app.resource_location + '/cut16_bis.png'), _("&Cutout Tool")) self.ncc_btn = self.toolbartools.addAction( @@ -2498,10 +2530,13 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.film_btn = self.toolbartools.addAction( QtGui.QIcon(self.app.resource_location + '/film16.png'), _("Film Tool")) self.solder_btn = self.toolbartools.addAction( - QtGui.QIcon(self.app.resource_location + '/solderpastebis32.png'), - _("SolderPaste Tool")) + QtGui.QIcon(self.app.resource_location + '/solderpastebis32.png'), _("SolderPaste Tool")) self.sub_btn = self.toolbartools.addAction( QtGui.QIcon(self.app.resource_location + '/sub32.png'), _("Subtract Tool")) + self.rules_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/rules32.png'), _("Rules Tool")) + self.optimal_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/open_excellon32.png'), _("Optimal Tool")) self.toolbartools.addSeparator() @@ -2834,6 +2869,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow): if key == QtCore.Qt.Key_G: self.app.on_toggle_axis() + # Locate in Object + if key == QtCore.Qt.Key_J: + self.app.on_locate(obj=self.app.collection.get_active()) + # Run Distance Minimum Tool if key == QtCore.Qt.Key_M: self.app.distance_min_tool.run() @@ -2882,6 +2921,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow): if key == Qt.Key_3: self.app.disable_other_plots() + # Align in Object Tool + if key == QtCore.Qt.Key_A: + self.app.align_objects_tool.run(toggle=True) + # Calculator Tool if key == QtCore.Qt.Key_C: self.app.calculator_tool.run(toggle=True) @@ -2906,6 +2949,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.app.on_toggle_grid_lines() return + # Align in Object Tool + if key == QtCore.Qt.Key_I: + self.app.edrills_tool.run(toggle=True) + # Fiducials Tool if key == QtCore.Qt.Key_J: self.app.fiducial_tool.run(toggle=True) diff --git a/flatcamTools/ToolAlignObjects.py b/flatcamTools/ToolAlignObjects.py index 7368b865..d1e77442 100644 --- a/flatcamTools/ToolAlignObjects.py +++ b/flatcamTools/ToolAlignObjects.py @@ -224,7 +224,6 @@ class AlignObjects(FlatCAMTool): self.aligned_old_fill_color = None self.aligned_old_line_color = None - def run(self, toggle=True): self.app.report_usage("ToolAlignObjects()") @@ -460,50 +459,6 @@ class AlignObjects(FlatCAMTool): angle = angle_dest - angle_start self.aligned_obj.rotate(angle=angle, point=origin_pt) - def execute(self): - aligned_name = self.object_combo.currentText() - - # Get source object. - try: - aligned_obj = self.app.collection.get_by_name(str(aligned_name)) - except Exception as e: - log.debug("AlignObjects.on_align() --> %s" % str(e)) - self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), aligned_name)) - return "Could not retrieve object: %s" % aligned_name - - if aligned_obj is None: - self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), aligned_obj)) - return "Object not found: %s" % aligned_obj - - aligner_name = self.box_combo.currentText() - - try: - aligner_obj = self.app.collection.get_by_name(aligner_name) - except Exception as e: - log.debug("AlignObjects.on_align() --> %s" % str(e)) - self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), aligner_name)) - return "Could not retrieve object: %s" % aligner_name - - if aligner_obj is None: - self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), aligner_name)) - - def align_job(): - pass - - proc = self.app.proc_container.new(_("Working...")) - - def job_thread(app_obj): - try: - align_job() - app_obj.inform.emit('[success] %s' % _("Panel created successfully.")) - except Exception as ee: - proc.done() - log.debug(str(ee)) - return - proc.done() - - self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]}) - def disconnect_cal_events(self): # restore the Grid snapping if it was active before if self.grid_status_memory is True: diff --git a/share/extract_drill16.png b/share/extract_drill16.png new file mode 100644 index 00000000..4b3a29ab Binary files /dev/null and b/share/extract_drill16.png differ diff --git a/share/extract_drill32.png b/share/extract_drill32.png new file mode 100644 index 00000000..41f740f2 Binary files /dev/null and b/share/extract_drill32.png differ diff --git a/share/locate16.png b/share/locate16.png new file mode 100644 index 00000000..1cdc85ed Binary files /dev/null and b/share/locate16.png differ diff --git a/share/locate32.png b/share/locate32.png new file mode 100644 index 00000000..66d630b0 Binary files /dev/null and b/share/locate32.png differ