diff --git a/FlatCAMObj.py b/FlatCAMObj.py index 39dbd8b9..83678a70 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -1172,7 +1172,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber): aperture = self.ui.apertures_table.item(row, 1).text() # self.plot_apertures(color='#2d4606bf', marked_aperture=aperture, visible=True) - self.plot_apertures(color='#FD6A02', marked_aperture=aperture, visible=True) + self.plot_apertures(color=self.app.defaults['global_sel_draw_color'], marked_aperture=aperture, visible=True) else: self.marked_rows.append(False) @@ -1209,7 +1209,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber): if mark_all: for aperture in self.apertures: # self.plot_apertures(color='#2d4606bf', marked_aperture=aperture, visible=True) - self.plot_apertures(color='#FD6A02', marked_aperture=aperture, visible=True) + self.plot_apertures(color=self.app.defaults['global_sel_draw_color'], marked_aperture=aperture, visible=True) else: self.clear_plot_apertures() diff --git a/README.md b/README.md index 37999b7b..9813648a 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,11 @@ CAD program, and create G-Code for Isolation routing. ================================================= +11.04.2019 + +- changed the color of the marked apertures to the global_selection_color +- Gerber Editor: added Transformation Tool and Rotation key shortcut + 10.04.2019 - Gerber Editor: added Add Track and Add Region functions diff --git a/flatcamEditors/FlatCAMGeoEditor.py b/flatcamEditors/FlatCAMGeoEditor.py index 07639a18..56a42993 100644 --- a/flatcamEditors/FlatCAMGeoEditor.py +++ b/flatcamEditors/FlatCAMGeoEditor.py @@ -2653,34 +2653,6 @@ class FlatCAMGeoEditor(QtCore.QObject): self.app = app self.canvas = app.plotcanvas - self.app.ui.geo_add_circle_menuitem.triggered.connect(lambda: self.select_tool('circle')) - self.app.ui.geo_add_arc_menuitem.triggered.connect(lambda: self.select_tool('arc')) - self.app.ui.geo_add_rectangle_menuitem.triggered.connect(lambda: self.select_tool('rectangle')) - self.app.ui.geo_add_polygon_menuitem.triggered.connect(lambda: self.select_tool('polygon')) - self.app.ui.geo_add_path_menuitem.triggered.connect(lambda: self.select_tool('path')) - self.app.ui.geo_add_text_menuitem.triggered.connect(lambda: self.select_tool('text')) - self.app.ui.geo_paint_menuitem.triggered.connect(self.on_paint_tool) - self.app.ui.geo_buffer_menuitem.triggered.connect(self.on_buffer_tool) - self.app.ui.geo_transform_menuitem.triggered.connect(self.on_transform_tool) - - self.app.ui.geo_delete_menuitem.triggered.connect(self.on_delete_btn) - self.app.ui.geo_union_menuitem.triggered.connect(self.union) - self.app.ui.geo_intersection_menuitem.triggered.connect(self.intersection) - self.app.ui.geo_subtract_menuitem.triggered.connect(self.subtract) - self.app.ui.geo_cutpath_menuitem.triggered.connect(self.cutpath) - self.app.ui.geo_copy_menuitem.triggered.connect(lambda: self.select_tool('copy')) - - self.app.ui.geo_union_btn.triggered.connect(self.union) - self.app.ui.geo_intersection_btn.triggered.connect(self.intersection) - self.app.ui.geo_subtract_btn.triggered.connect(self.subtract) - self.app.ui.geo_cutpath_btn.triggered.connect(self.cutpath) - self.app.ui.geo_delete_btn.triggered.connect(self.on_delete_btn) - - self.app.ui.geo_move_menuitem.triggered.connect(self.on_move) - self.app.ui.geo_cornersnap_menuitem.triggered.connect(self.on_corner_snap) - - self.transform_complete.connect(self.on_transform_complete) - ## Toolbar events and properties self.tools = { "select": {"button": self.app.ui.geo_select_btn, @@ -2814,15 +2786,43 @@ class FlatCAMGeoEditor(QtCore.QObject): self.app.ui.snap_max_dist_entry.textChanged.connect( lambda: entry2option("snap_max", self.app.ui.snap_max_dist_entry)) - # store the status of the editor so the Delete at object level will not work until the edit is finished - self.editor_active = False - # if using Paint store here the tool diameter used self.paint_tooldia = None self.paint_tool = PaintOptionsTool(self.app, self) self.transform_tool = TransformEditorTool(self.app, self) + self.app.ui.geo_add_circle_menuitem.triggered.connect(lambda: self.select_tool('circle')) + self.app.ui.geo_add_arc_menuitem.triggered.connect(lambda: self.select_tool('arc')) + self.app.ui.geo_add_rectangle_menuitem.triggered.connect(lambda: self.select_tool('rectangle')) + self.app.ui.geo_add_polygon_menuitem.triggered.connect(lambda: self.select_tool('polygon')) + self.app.ui.geo_add_path_menuitem.triggered.connect(lambda: self.select_tool('path')) + self.app.ui.geo_add_text_menuitem.triggered.connect(lambda: self.select_tool('text')) + self.app.ui.geo_paint_menuitem.triggered.connect(self.on_paint_tool) + self.app.ui.geo_buffer_menuitem.triggered.connect(self.on_buffer_tool) + self.app.ui.geo_transform_menuitem.triggered.connect(self.transform_tool.run) + + self.app.ui.geo_delete_menuitem.triggered.connect(self.on_delete_btn) + self.app.ui.geo_union_menuitem.triggered.connect(self.union) + self.app.ui.geo_intersection_menuitem.triggered.connect(self.intersection) + self.app.ui.geo_subtract_menuitem.triggered.connect(self.subtract) + self.app.ui.geo_cutpath_menuitem.triggered.connect(self.cutpath) + self.app.ui.geo_copy_menuitem.triggered.connect(lambda: self.select_tool('copy')) + + self.app.ui.geo_union_btn.triggered.connect(self.union) + self.app.ui.geo_intersection_btn.triggered.connect(self.intersection) + self.app.ui.geo_subtract_btn.triggered.connect(self.subtract) + self.app.ui.geo_cutpath_btn.triggered.connect(self.cutpath) + self.app.ui.geo_delete_btn.triggered.connect(self.on_delete_btn) + + self.app.ui.geo_move_menuitem.triggered.connect(self.on_move) + self.app.ui.geo_cornersnap_menuitem.triggered.connect(self.on_corner_snap) + + self.transform_complete.connect(self.on_transform_complete) + + # store the status of the editor so the Delete at object level will not work until the edit is finished + self.editor_active = False + def pool_recreated(self, pool): self.shapes.pool = pool self.tool_shape.pool = pool @@ -2856,8 +2856,7 @@ class FlatCAMGeoEditor(QtCore.QObject): self.app.ui.geo_edit_toolbar.setDisabled(False) self.app.ui.geo_edit_toolbar.setVisible(True) - self.app.ui.grb_edit_toolbar.setDisabled(False) - self.app.ui.grb_edit_toolbar.setVisible(True) + self.app.ui.snap_toolbar.setDisabled(False) # prevent the user to change anything in the Selected Tab while the Geo Editor is active @@ -2937,7 +2936,6 @@ class FlatCAMGeoEditor(QtCore.QObject): self.canvas.vis_connect('mouse_move', self.on_canvas_move) self.canvas.vis_connect('mouse_release', self.on_canvas_click_release) - def disconnect_canvas_event_handlers(self): self.canvas.vis_disconnect('mouse_press', self.on_canvas_click) @@ -3089,10 +3087,6 @@ class FlatCAMGeoEditor(QtCore.QObject): paint_tool = PaintOptionsTool(self.app, self) paint_tool.run() - def on_transform_tool(self): - transform_tool = TransformEditorTool(self.app, self) - transform_tool.run() - def on_tool_select(self, tool): """ Behavior of the toolbar. Tool initialization. diff --git a/flatcamEditors/FlatCAMGrbEditor.py b/flatcamEditors/FlatCAMGrbEditor.py index c3078292..3d9e6075 100644 --- a/flatcamEditors/FlatCAMGrbEditor.py +++ b/flatcamEditors/FlatCAMGrbEditor.py @@ -12,7 +12,7 @@ import copy from camlib import * from flatcamGUI.GUIElements import FCEntry, FCComboBox, FCTable, FCDoubleSpinner, LengthEntry, RadioSet, \ - SpinBoxDelegate, EvalEntry + SpinBoxDelegate, EvalEntry, EvalEntry2, FCInputDialog, FCButton, OptionalInputSection, FCCheckBox from flatcamEditors.FlatCAMGeoEditor import FCShapeTool, DrawTool, DrawToolShape, DrawToolUtilityShape, FlatCAMGeoEditor from FlatCAMObj import FlatCAMGerber from FlatCAMTool import FlatCAMTool @@ -242,9 +242,9 @@ class FCScale(FCShapeTool): if self.draw_app.app.ui.splitter.sizes()[0] == 0: self.draw_app.app.ui.splitter.setSizes([1, 1]) - self.activate() + self.activate_scale() - def activate(self): + def activate_scale(self): self.draw_app.hide_tool('all') self.draw_app.scale_tool_frame.show() @@ -254,7 +254,7 @@ class FCScale(FCShapeTool): pass self.draw_app.scale_button.clicked.connect(self.on_scale_click) - def deactivate(self): + def deactivate_scale(self): self.draw_app.scale_button.clicked.disconnect() self.complete = True self.draw_app.select_tool("select") @@ -262,7 +262,7 @@ class FCScale(FCShapeTool): def on_scale_click(self): self.draw_app.on_scale() - self.deactivate() + self.deactivate_scale() class FCBuffer(FCShapeTool): @@ -279,9 +279,9 @@ class FCBuffer(FCShapeTool): if self.draw_app.app.ui.splitter.sizes()[0] == 0: self.draw_app.app.ui.splitter.setSizes([1, 1]) - self.activate() + self.activate_buffer() - def activate(self): + def activate_buffer(self): self.draw_app.hide_tool('all') self.draw_app.buffer_tool_frame.show() @@ -291,7 +291,7 @@ class FCBuffer(FCShapeTool): pass self.draw_app.buffer_button.clicked.connect(self.on_buffer_click) - def deactivate(self): + def deactivate_buffer(self): self.draw_app.buffer_button.clicked.disconnect() self.complete = True self.draw_app.select_tool("select") @@ -299,7 +299,7 @@ class FCBuffer(FCShapeTool): def on_buffer_click(self): self.draw_app.on_buffer() - self.deactivate() + self.deactivate_buffer() class FCApertureMove(FCShapeTool): @@ -492,6 +492,20 @@ class FCApertureSelect(DrawTool): return "" +class FCTransform(FCShapeTool): + def __init__(self, draw_app): + FCShapeTool.__init__(self, draw_app) + self.name = 'transformation' + + # self.shape_buffer = self.draw_app.shape_buffer + self.draw_app = draw_app + self.app = draw_app.app + + self.start_msg = _("Shape transformations ...") + self.origin = (0, 0) + self.draw_app.transform_tool.run() + + class FlatCAMGrbEditor(QtCore.QObject): draw_shape_idx = -1 @@ -759,6 +773,8 @@ class FlatCAMGrbEditor(QtCore.QObject): "constructor": FCScale}, "copy": {"button": self.app.ui.aperture_copy_btn, "constructor": FCApertureCopy}, + "transform": {"button": self.app.ui.grb_transform_btn, + "constructor": FCTransform}, "move": {"button": self.app.ui.aperture_move_btn, "constructor": FCApertureMove}, } @@ -794,32 +810,6 @@ class FlatCAMGrbEditor(QtCore.QObject): # this var will store the state of the toolbar before starting the editor self.toolbar_old_state = False - # Signals - self.buffer_button.clicked.connect(self.on_buffer) - self.scale_button.clicked.connect(self.on_scale) - - self.app.ui.delete_drill_btn.triggered.connect(self.on_delete_btn) - self.name_entry.returnPressed.connect(self.on_name_activate) - - self.aptype_cb.currentIndexChanged[str].connect(self.on_aptype_changed) - - self.addaperture_btn.clicked.connect(self.on_aperture_add) - self.delaperture_btn.clicked.connect(self.on_aperture_delete) - self.apertures_table.cellPressed.connect(self.on_row_selected) - - self.app.ui.grb_add_pad_menuitem.triggered.connect(self.on_pad_add) - self.app.ui.grb_add_track_menuitem.triggered.connect(self.on_track_add) - self.app.ui.grb_add_region_menuitem.triggered.connect(self.on_region_add) - - self.app.ui.grb_add_buffer_menuitem.triggered.connect(self.on_buffer) - self.app.ui.grb_add_scale_menuitem.triggered.connect(self.on_scale) - - 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.on_move_button) - - # Init GUI self.apdim_lbl.hide() self.apdim_entry.hide() @@ -889,6 +879,34 @@ class FlatCAMGrbEditor(QtCore.QObject): def entry2option(option, entry): self.options[option] = float(entry.text()) + self.transform_tool = TransformEditorTool(self.app, self) + + # Signals + self.buffer_button.clicked.connect(self.on_buffer) + self.scale_button.clicked.connect(self.on_scale) + + self.app.ui.delete_drill_btn.triggered.connect(self.on_delete_btn) + self.name_entry.returnPressed.connect(self.on_name_activate) + + self.aptype_cb.currentIndexChanged[str].connect(self.on_aptype_changed) + + self.addaperture_btn.clicked.connect(self.on_aperture_add) + self.delaperture_btn.clicked.connect(self.on_aperture_delete) + self.apertures_table.cellPressed.connect(self.on_row_selected) + + self.app.ui.grb_add_pad_menuitem.triggered.connect(self.on_pad_add) + self.app.ui.grb_add_track_menuitem.triggered.connect(self.on_track_add) + self.app.ui.grb_add_region_menuitem.triggered.connect(self.on_region_add) + + self.app.ui.grb_add_buffer_menuitem.triggered.connect(self.on_buffer) + self.app.ui.grb_add_scale_menuitem.triggered.connect(self.on_scale) + self.app.ui.grb_transform_menuitem.triggered.connect(self.transform_tool.run) + + 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.on_move_button) + # store the status of the editor so the Delete at object level will not work until the edit is finished self.editor_active = False @@ -1256,7 +1274,7 @@ class FlatCAMGrbEditor(QtCore.QObject): self.apdim_entry.hide() self.apsize_entry.setReadOnly(False) - def activate(self): + def activate_grb_editor(self): self.connect_canvas_event_handlers() # init working objects @@ -1294,7 +1312,7 @@ class FlatCAMGrbEditor(QtCore.QObject): # Tell the App that the editor is active self.editor_active = True - def deactivate(self): + def deactivate_grb_editor(self): self.disconnect_canvas_event_handlers() self.clear() self.app.ui.grb_edit_toolbar.setDisabled(True) @@ -1394,8 +1412,8 @@ class FlatCAMGrbEditor(QtCore.QObject): :return: None """ - self.deactivate() - self.activate() + self.deactivate_grb_editor() + self.activate_grb_editor() # create a reference to the source object self.gerber_obj = orig_grb_obj @@ -1445,9 +1463,6 @@ class FlatCAMGrbEditor(QtCore.QObject): except ValueError: break - for k, v in self.gerber_obj.apertures.items(): - print(k, v) - for apid in self.gerber_obj.apertures: self.grb_plot_promises.append(apid) self.app.worker_task.emit({'fcn': job_thread, 'params': [self, apid]}) @@ -2251,4 +2266,972 @@ class FlatCAMGrbEditor(QtCore.QObject): if tool_name == 'scale' or tool_name == 'all': self.scale_tool_frame.hide() - self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab) \ No newline at end of file + self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab) + +class TransformEditorTool(FlatCAMTool): + """ + Inputs to specify how to paint the selected polygons. + """ + + toolName = _("Transform Tool") + rotateName = _("Rotate") + skewName = _("Skew/Shear") + scaleName = _("Scale") + flipName = _("Mirror (Flip)") + offsetName = _("Offset") + + def __init__(self, app, draw_app): + FlatCAMTool.__init__(self, app) + + self.app = app + self.draw_app = draw_app + + self.transform_lay = QtWidgets.QVBoxLayout() + self.layout.addLayout(self.transform_lay) + ## Title + title_label = QtWidgets.QLabel("%s" % (_('Editor %s') % self.toolName)) + title_label.setStyleSheet(""" + QLabel + { + font-size: 16px; + font-weight: bold; + } + """) + self.transform_lay.addWidget(title_label) + + self.empty_label = QtWidgets.QLabel("") + self.empty_label.setFixedWidth(50) + + self.empty_label1 = QtWidgets.QLabel("") + self.empty_label1.setFixedWidth(70) + self.empty_label2 = QtWidgets.QLabel("") + self.empty_label2.setFixedWidth(70) + self.empty_label3 = QtWidgets.QLabel("") + self.empty_label3.setFixedWidth(70) + self.empty_label4 = QtWidgets.QLabel("") + self.empty_label4.setFixedWidth(70) + self.transform_lay.addWidget(self.empty_label) + + ## Rotate Title + rotate_title_label = QtWidgets.QLabel("%s" % self.rotateName) + self.transform_lay.addWidget(rotate_title_label) + + ## Layout + form_layout = QtWidgets.QFormLayout() + self.transform_lay.addLayout(form_layout) + form_child = QtWidgets.QHBoxLayout() + + self.rotate_label = QtWidgets.QLabel(_("Angle:")) + self.rotate_label.setToolTip( + _("Angle for Rotation action, in degrees.\n" + "Float number between -360 and 359.\n" + "Positive numbers for CW motion.\n" + "Negative numbers for CCW motion.") + ) + self.rotate_label.setFixedWidth(50) + + self.rotate_entry = FCEntry() + # self.rotate_entry.setFixedWidth(60) + self.rotate_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) + + self.rotate_button = FCButton() + self.rotate_button.set_value(_("Rotate")) + self.rotate_button.setToolTip( + _("Rotate the selected shape(s).\n" + "The point of reference is the middle of\n" + "the bounding box for all selected shapes.") + ) + self.rotate_button.setFixedWidth(60) + + form_child.addWidget(self.rotate_entry) + form_child.addWidget(self.rotate_button) + + form_layout.addRow(self.rotate_label, form_child) + + self.transform_lay.addWidget(self.empty_label1) + + ## Skew Title + skew_title_label = QtWidgets.QLabel("%s" % self.skewName) + self.transform_lay.addWidget(skew_title_label) + + ## Form Layout + form1_layout = QtWidgets.QFormLayout() + self.transform_lay.addLayout(form1_layout) + form1_child_1 = QtWidgets.QHBoxLayout() + form1_child_2 = QtWidgets.QHBoxLayout() + + self.skewx_label = QtWidgets.QLabel(_("Angle X:")) + self.skewx_label.setToolTip( + _("Angle for Skew action, in degrees.\n" + "Float number between -360 and 359.") + ) + self.skewx_label.setFixedWidth(50) + self.skewx_entry = FCEntry() + self.skewx_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) + # self.skewx_entry.setFixedWidth(60) + + self.skewx_button = FCButton() + self.skewx_button.set_value(_("Skew X")) + self.skewx_button.setToolTip( + _("Skew/shear the selected shape(s).\n" + "The point of reference is the middle of\n" + "the bounding box for all selected shapes.")) + self.skewx_button.setFixedWidth(60) + + self.skewy_label = QtWidgets.QLabel(_("Angle Y:")) + self.skewy_label.setToolTip( + _("Angle for Skew action, in degrees.\n" + "Float number between -360 and 359.") + ) + self.skewy_label.setFixedWidth(50) + self.skewy_entry = FCEntry() + self.skewy_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) + # self.skewy_entry.setFixedWidth(60) + + self.skewy_button = FCButton() + self.skewy_button.set_value(_("Skew Y")) + self.skewy_button.setToolTip( + _("Skew/shear the selected shape(s).\n" + "The point of reference is the middle of\n" + "the bounding box for all selected shapes.")) + self.skewy_button.setFixedWidth(60) + + form1_child_1.addWidget(self.skewx_entry) + form1_child_1.addWidget(self.skewx_button) + + form1_child_2.addWidget(self.skewy_entry) + form1_child_2.addWidget(self.skewy_button) + + form1_layout.addRow(self.skewx_label, form1_child_1) + form1_layout.addRow(self.skewy_label, form1_child_2) + + self.transform_lay.addWidget(self.empty_label2) + + ## Scale Title + scale_title_label = QtWidgets.QLabel("%s" % self.scaleName) + self.transform_lay.addWidget(scale_title_label) + + ## Form Layout + form2_layout = QtWidgets.QFormLayout() + self.transform_lay.addLayout(form2_layout) + form2_child_1 = QtWidgets.QHBoxLayout() + form2_child_2 = QtWidgets.QHBoxLayout() + + self.scalex_label = QtWidgets.QLabel(_("Factor X:")) + self.scalex_label.setToolTip( + _("Factor for Scale action over X axis.") + ) + self.scalex_label.setFixedWidth(50) + self.scalex_entry = FCEntry() + self.scalex_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) + # self.scalex_entry.setFixedWidth(60) + + self.scalex_button = FCButton() + self.scalex_button.set_value(_("Scale X")) + self.scalex_button.setToolTip( + _("Scale the selected shape(s).\n" + "The point of reference depends on \n" + "the Scale reference checkbox state.")) + self.scalex_button.setFixedWidth(60) + + self.scaley_label = QtWidgets.QLabel(_("Factor Y:")) + self.scaley_label.setToolTip( + _("Factor for Scale action over Y axis.") + ) + self.scaley_label.setFixedWidth(50) + self.scaley_entry = FCEntry() + self.scaley_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) + # self.scaley_entry.setFixedWidth(60) + + self.scaley_button = FCButton() + self.scaley_button.set_value(_("Scale Y")) + self.scaley_button.setToolTip( + _("Scale the selected shape(s).\n" + "The point of reference depends on \n" + "the Scale reference checkbox state.")) + self.scaley_button.setFixedWidth(60) + + self.scale_link_cb = FCCheckBox() + self.scale_link_cb.set_value(True) + self.scale_link_cb.setText(_("Link")) + self.scale_link_cb.setToolTip( + _("Scale the selected shape(s)\n" + "using the Scale Factor X for both axis.")) + self.scale_link_cb.setFixedWidth(50) + + self.scale_zero_ref_cb = FCCheckBox() + self.scale_zero_ref_cb.set_value(True) + self.scale_zero_ref_cb.setText(_("Scale Reference")) + self.scale_zero_ref_cb.setToolTip( + _("Scale the selected shape(s)\n" + "using the origin reference when checked,\n" + "and the center of the biggest bounding box\n" + "of the selected shapes when unchecked.")) + + form2_child_1.addWidget(self.scalex_entry) + form2_child_1.addWidget(self.scalex_button) + + form2_child_2.addWidget(self.scaley_entry) + form2_child_2.addWidget(self.scaley_button) + + form2_layout.addRow(self.scalex_label, form2_child_1) + form2_layout.addRow(self.scaley_label, form2_child_2) + form2_layout.addRow(self.scale_link_cb, self.scale_zero_ref_cb) + self.ois_scale = OptionalInputSection(self.scale_link_cb, [self.scaley_entry, self.scaley_button], + logic=False) + + self.transform_lay.addWidget(self.empty_label3) + + ## Offset Title + offset_title_label = QtWidgets.QLabel("%s" % self.offsetName) + self.transform_lay.addWidget(offset_title_label) + + ## Form Layout + form3_layout = QtWidgets.QFormLayout() + self.transform_lay.addLayout(form3_layout) + form3_child_1 = QtWidgets.QHBoxLayout() + form3_child_2 = QtWidgets.QHBoxLayout() + + self.offx_label = QtWidgets.QLabel(_("Value X:")) + self.offx_label.setToolTip( + _("Value for Offset action on X axis.") + ) + self.offx_label.setFixedWidth(50) + self.offx_entry = FCEntry() + self.offx_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) + # self.offx_entry.setFixedWidth(60) + + self.offx_button = FCButton() + self.offx_button.set_value(_("Offset X")) + self.offx_button.setToolTip( + _("Offset the selected shape(s).\n" + "The point of reference is the middle of\n" + "the bounding box for all selected shapes.\n") + ) + self.offx_button.setFixedWidth(60) + + self.offy_label = QtWidgets.QLabel(_("Value Y:")) + self.offy_label.setToolTip( + _("Value for Offset action on Y axis.") + ) + self.offy_label.setFixedWidth(50) + self.offy_entry = FCEntry() + self.offy_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) + # self.offy_entry.setFixedWidth(60) + + self.offy_button = FCButton() + self.offy_button.set_value(_("Offset Y")) + self.offy_button.setToolTip( + _("Offset the selected shape(s).\n" + "The point of reference is the middle of\n" + "the bounding box for all selected shapes.\n") + ) + self.offy_button.setFixedWidth(60) + + form3_child_1.addWidget(self.offx_entry) + form3_child_1.addWidget(self.offx_button) + + form3_child_2.addWidget(self.offy_entry) + form3_child_2.addWidget(self.offy_button) + + form3_layout.addRow(self.offx_label, form3_child_1) + form3_layout.addRow(self.offy_label, form3_child_2) + + self.transform_lay.addWidget(self.empty_label4) + + ## Flip Title + flip_title_label = QtWidgets.QLabel("%s" % self.flipName) + self.transform_lay.addWidget(flip_title_label) + + ## Form Layout + form4_layout = QtWidgets.QFormLayout() + form4_child_hlay = QtWidgets.QHBoxLayout() + self.transform_lay.addLayout(form4_child_hlay) + self.transform_lay.addLayout(form4_layout) + form4_child_1 = QtWidgets.QHBoxLayout() + + self.flipx_button = FCButton() + self.flipx_button.set_value(_("Flip on X")) + self.flipx_button.setToolTip( + _("Flip the selected shape(s) over the X axis.\n" + "Does not create a new shape.") + ) + self.flipx_button.setFixedWidth(60) + + self.flipy_button = FCButton() + self.flipy_button.set_value(_("Flip on Y")) + self.flipy_button.setToolTip( + _("Flip the selected shape(s) over the X axis.\n" + "Does not create a new shape.") + ) + self.flipy_button.setFixedWidth(60) + + self.flip_ref_cb = FCCheckBox() + self.flip_ref_cb.set_value(True) + self.flip_ref_cb.setText(_("Ref Pt")) + self.flip_ref_cb.setToolTip( + _("Flip the selected shape(s)\n" + "around the point in Point Entry Field.\n" + "\n" + "The point coordinates can be captured by\n" + "left click on canvas together with pressing\n" + "SHIFT key. \n" + "Then click Add button to insert coordinates.\n" + "Or enter the coords in format (x, y) in the\n" + "Point Entry field and click Flip on X(Y)") + ) + self.flip_ref_cb.setFixedWidth(50) + + self.flip_ref_label = QtWidgets.QLabel(_("Point:")) + self.flip_ref_label.setToolTip( + _("Coordinates in format (x, y) used as reference for mirroring.\n" + "The 'x' in (x, y) will be used when using Flip on X and\n" + "the 'y' in (x, y) will be used when using Flip on Y.") + ) + self.flip_ref_label.setFixedWidth(50) + self.flip_ref_entry = EvalEntry2("(0, 0)") + self.flip_ref_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) + # self.flip_ref_entry.setFixedWidth(60) + + self.flip_ref_button = FCButton() + self.flip_ref_button.set_value(_("Add")) + self.flip_ref_button.setToolTip( + _("The point coordinates can be captured by\n" + "left click on canvas together with pressing\n" + "SHIFT key. Then click Add button to insert.") + ) + self.flip_ref_button.setFixedWidth(60) + + form4_child_hlay.addStretch() + form4_child_hlay.addWidget(self.flipx_button) + form4_child_hlay.addWidget(self.flipy_button) + + form4_child_1.addWidget(self.flip_ref_entry) + form4_child_1.addWidget(self.flip_ref_button) + + form4_layout.addRow(self.flip_ref_cb) + form4_layout.addRow(self.flip_ref_label, form4_child_1) + self.ois_flip = OptionalInputSection(self.flip_ref_cb, + [self.flip_ref_entry, self.flip_ref_button], logic=True) + + self.transform_lay.addStretch() + + ## Signals + self.rotate_button.clicked.connect(self.on_rotate) + self.skewx_button.clicked.connect(self.on_skewx) + self.skewy_button.clicked.connect(self.on_skewy) + self.scalex_button.clicked.connect(self.on_scalex) + self.scaley_button.clicked.connect(self.on_scaley) + self.offx_button.clicked.connect(self.on_offx) + self.offy_button.clicked.connect(self.on_offy) + self.flipx_button.clicked.connect(self.on_flipx) + self.flipy_button.clicked.connect(self.on_flipy) + self.flip_ref_button.clicked.connect(self.on_flip_add_coords) + + self.rotate_entry.returnPressed.connect(self.on_rotate) + self.skewx_entry.returnPressed.connect(self.on_skewx) + self.skewy_entry.returnPressed.connect(self.on_skewy) + self.scalex_entry.returnPressed.connect(self.on_scalex) + self.scaley_entry.returnPressed.connect(self.on_scaley) + self.offx_entry.returnPressed.connect(self.on_offx) + self.offy_entry.returnPressed.connect(self.on_offy) + + self.set_tool_ui() + + def run(self): + self.app.report_usage("Geo Editor Transform Tool()") + FlatCAMTool.run(self) + self.set_tool_ui() + + # if the splitter us hidden, display it + if self.app.ui.splitter.sizes()[0] == 0: + self.app.ui.splitter.setSizes([1, 1]) + + self.app.ui.notebook.setTabText(2, _("Transform Tool")) + + def install(self, icon=None, separator=None, **kwargs): + FlatCAMTool.install(self, icon, separator, shortcut='ALT+T', **kwargs) + + def set_tool_ui(self): + ## Initialize form + if self.app.defaults["tools_transform_rotate"]: + self.rotate_entry.set_value(self.app.defaults["tools_transform_rotate"]) + else: + self.rotate_entry.set_value(0.0) + + if self.app.defaults["tools_transform_skew_x"]: + self.skewx_entry.set_value(self.app.defaults["tools_transform_skew_x"]) + else: + self.skewx_entry.set_value(0.0) + + if self.app.defaults["tools_transform_skew_y"]: + self.skewy_entry.set_value(self.app.defaults["tools_transform_skew_y"]) + else: + self.skewy_entry.set_value(0.0) + + if self.app.defaults["tools_transform_scale_x"]: + self.scalex_entry.set_value(self.app.defaults["tools_transform_scale_x"]) + else: + self.scalex_entry.set_value(1.0) + + if self.app.defaults["tools_transform_scale_y"]: + self.scaley_entry.set_value(self.app.defaults["tools_transform_scale_y"]) + else: + self.scaley_entry.set_value(1.0) + + if self.app.defaults["tools_transform_scale_link"]: + self.scale_link_cb.set_value(self.app.defaults["tools_transform_scale_link"]) + else: + self.scale_link_cb.set_value(True) + + if self.app.defaults["tools_transform_scale_reference"]: + self.scale_zero_ref_cb.set_value(self.app.defaults["tools_transform_scale_reference"]) + else: + self.scale_zero_ref_cb.set_value(True) + + if self.app.defaults["tools_transform_offset_x"]: + self.offx_entry.set_value(self.app.defaults["tools_transform_offset_x"]) + else: + self.offx_entry.set_value(0.0) + + if self.app.defaults["tools_transform_offset_y"]: + self.offy_entry.set_value(self.app.defaults["tools_transform_offset_y"]) + else: + self.offy_entry.set_value(0.0) + + if self.app.defaults["tools_transform_mirror_reference"]: + self.flip_ref_cb.set_value(self.app.defaults["tools_transform_mirror_reference"]) + else: + self.flip_ref_cb.set_value(False) + + if self.app.defaults["tools_transform_mirror_point"]: + self.flip_ref_entry.set_value(self.app.defaults["tools_transform_mirror_point"]) + else: + self.flip_ref_entry.set_value((0, 0)) + + def template(self): + if not self.fcdraw.selected: + self.app.inform.emit(_("[WARNING_NOTCL] Transformation cancelled. No shape selected.")) + return + + self.draw_app.select_tool("select") + self.app.ui.notebook.setTabText(2, "Tools") + self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab) + + self.app.ui.splitter.setSizes([0, 1]) + + def on_rotate(self, sig=None, val=None): + if val: + value = val + else: + try: + value = float(self.rotate_entry.get_value()) + except ValueError: + # try to convert comma to decimal point. if it's still not working error message and return + try: + value = float(self.rotate_entry.get_value().replace(',', '.')) + except ValueError: + self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Rotate, " + "use a number.")) + return + self.app.worker_task.emit({'fcn': self.on_rotate_action, + 'params': [value]}) + # self.on_rotate_action(value) + return + + def on_flipx(self): + # self.on_flip("Y") + axis = 'Y' + self.app.worker_task.emit({'fcn': self.on_flip, + 'params': [axis]}) + return + + def on_flipy(self): + # self.on_flip("X") + axis = 'X' + self.app.worker_task.emit({'fcn': self.on_flip, + 'params': [axis]}) + return + + def on_flip_add_coords(self): + val = self.app.clipboard.text() + self.flip_ref_entry.set_value(val) + + def on_skewx(self, sig=None, val=None): + if val: + value = val + else: + try: + value = float(self.skewx_entry.get_value()) + except ValueError: + # try to convert comma to decimal point. if it's still not working error message and return + try: + value = float(self.skewx_entry.get_value().replace(',', '.')) + except ValueError: + self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Skew X, " + "use a number.")) + return + + # self.on_skew("X", value) + axis = 'X' + self.app.worker_task.emit({'fcn': self.on_skew, + 'params': [axis, value]}) + return + + def on_skewy(self, sig=None, val=None): + if val: + value = val + else: + try: + value = float(self.skewy_entry.get_value()) + except ValueError: + # try to convert comma to decimal point. if it's still not working error message and return + try: + value = float(self.skewy_entry.get_value().replace(',', '.')) + except ValueError: + self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Skew Y, " + "use a number.")) + return + + # self.on_skew("Y", value) + axis = 'Y' + self.app.worker_task.emit({'fcn': self.on_skew, + 'params': [axis, value]}) + return + + def on_scalex(self, sig=None, val=None): + if val: + xvalue = val + else: + try: + xvalue = float(self.scalex_entry.get_value()) + except ValueError: + # try to convert comma to decimal point. if it's still not working error message and return + try: + xvalue = float(self.scalex_entry.get_value().replace(',', '.')) + except ValueError: + self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Scale X, " + "use a number.")) + return + + # scaling to zero has no sense so we remove it, because scaling with 1 does nothing + if xvalue == 0: + xvalue = 1 + if self.scale_link_cb.get_value(): + yvalue = xvalue + else: + yvalue = 1 + + axis = 'X' + point = (0, 0) + if self.scale_zero_ref_cb.get_value(): + self.app.worker_task.emit({'fcn': self.on_scale, + 'params': [axis, xvalue, yvalue, point]}) + # self.on_scale("X", xvalue, yvalue, point=(0,0)) + else: + # self.on_scale("X", xvalue, yvalue) + self.app.worker_task.emit({'fcn': self.on_scale, + 'params': [axis, xvalue, yvalue]}) + + return + + def on_scaley(self, sig=None, val=None): + xvalue = 1 + if val: + yvalue = val + else: + try: + yvalue = float(self.scaley_entry.get_value()) + except ValueError: + # try to convert comma to decimal point. if it's still not working error message and return + try: + yvalue = float(self.scaley_entry.get_value().replace(',', '.')) + except ValueError: + self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Scale Y, " + "use a number.")) + return + + # scaling to zero has no sense so we remove it, because scaling with 1 does nothing + if yvalue == 0: + yvalue = 1 + + axis = 'Y' + point = (0, 0) + if self.scale_zero_ref_cb.get_value(): + self.app.worker_task.emit({'fcn': self.on_scale, + 'params': [axis, xvalue, yvalue, point]}) + # self.on_scale("Y", xvalue, yvalue, point=(0,0)) + else: + # self.on_scale("Y", xvalue, yvalue) + self.app.worker_task.emit({'fcn': self.on_scale, + 'params': [axis, xvalue, yvalue]}) + + return + + def on_offx(self, sig=None, val=None): + if val: + value = val + else: + try: + value = float(self.offx_entry.get_value()) + except ValueError: + # try to convert comma to decimal point. if it's still not working error message and return + try: + value = float(self.offx_entry.get_value().replace(',', '.')) + except ValueError: + self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Offset X, " + "use a number.")) + return + + # self.on_offset("X", value) + axis = 'X' + self.app.worker_task.emit({'fcn': self.on_offset, + 'params': [axis, value]}) + return + + def on_offy(self, sig=None, val=None): + if val: + value = val + else: + try: + value = float(self.offy_entry.get_value()) + except ValueError: + # try to convert comma to decimal point. if it's still not working error message and return + try: + value = float(self.offy_entry.get_value().replace(',', '.')) + except ValueError: + self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Offset Y, " + "use a number.")) + return + + # self.on_offset("Y", value) + axis = 'Y' + self.app.worker_task.emit({'fcn': self.on_offset, + 'params': [axis, value]}) + return + + def on_rotate_action(self, num): + shape_list = self.draw_app.selected + xminlist = [] + yminlist = [] + xmaxlist = [] + ymaxlist = [] + + if not shape_list: + self.app.inform.emit(_("[WARNING_NOTCL] No shape selected. Please Select a shape to rotate!")) + return + else: + with self.app.proc_container.new(_("Appying Rotate")): + try: + # first get a bounding box to fit all + for sha in shape_list: + xmin, ymin, xmax, ymax = sha.bounds() + xminlist.append(xmin) + yminlist.append(ymin) + xmaxlist.append(xmax) + ymaxlist.append(ymax) + + # get the minimum x,y and maximum x,y for all objects selected + xminimal = min(xminlist) + yminimal = min(yminlist) + xmaximal = max(xmaxlist) + ymaximal = max(ymaxlist) + + self.app.progress.emit(20) + + for sel_sha in shape_list: + px = 0.5 * (xminimal + xmaximal) + py = 0.5 * (yminimal + ymaximal) + + sel_sha.rotate(-num, point=(px, py)) + self.draw_app.plot_all() + # self.draw_app.add_shape(DrawToolShape(sel_sha.geo)) + + # self.draw_app.transform_complete.emit() + + self.app.inform.emit(_("[success] Done. Rotate completed.")) + + self.app.progress.emit(100) + + except Exception as e: + self.app.inform.emit(_("[ERROR_NOTCL] Due of %s, rotation movement was not executed.") % str(e)) + return + + def on_flip(self, axis): + shape_list = self.draw_app.selected + xminlist = [] + yminlist = [] + xmaxlist = [] + ymaxlist = [] + + if not shape_list: + self.app.inform.emit(_("[WARNING_NOTCL] No shape selected. Please Select a shape to flip!")) + return + else: + with self.app.proc_container.new(_("Applying Flip")): + try: + # get mirroring coords from the point entry + if self.flip_ref_cb.isChecked(): + px, py = eval('{}'.format(self.flip_ref_entry.text())) + # get mirroing coords from the center of an all-enclosing bounding box + else: + # first get a bounding box to fit all + for sha in shape_list: + xmin, ymin, xmax, ymax = sha.bounds() + xminlist.append(xmin) + yminlist.append(ymin) + xmaxlist.append(xmax) + ymaxlist.append(ymax) + + # get the minimum x,y and maximum x,y for all objects selected + xminimal = min(xminlist) + yminimal = min(yminlist) + xmaximal = max(xmaxlist) + ymaximal = max(ymaxlist) + + px = 0.5 * (xminimal + xmaximal) + py = 0.5 * (yminimal + ymaximal) + + self.app.progress.emit(20) + + # execute mirroring + for sha in shape_list: + if axis is 'X': + sha.mirror('X', (px, py)) + self.app.inform.emit(_('[success] Flip on the Y axis done ...')) + elif axis is 'Y': + sha.mirror('Y', (px, py)) + self.app.inform.emit(_('[success] Flip on the X axis done ...')) + self.draw_app.plot_all() + + # self.draw_app.add_shape(DrawToolShape(sha.geo)) + # + # self.draw_app.transform_complete.emit() + + self.app.progress.emit(100) + + except Exception as e: + self.app.inform.emit(_("[ERROR_NOTCL] Due of %s, Flip action was not executed.") % str(e)) + return + + def on_skew(self, axis, num): + shape_list = self.draw_app.selected + xminlist = [] + yminlist = [] + + if not shape_list: + self.app.inform.emit(_("[WARNING_NOTCL] No shape selected. Please Select a shape to shear/skew!")) + return + else: + with self.app.proc_container.new(_("Applying Skew")): + try: + # first get a bounding box to fit all + for sha in shape_list: + xmin, ymin, xmax, ymax = sha.bounds() + xminlist.append(xmin) + yminlist.append(ymin) + + # get the minimum x,y and maximum x,y for all objects selected + xminimal = min(xminlist) + yminimal = min(yminlist) + + self.app.progress.emit(20) + + for sha in shape_list: + if axis is 'X': + sha.skew(num, 0, point=(xminimal, yminimal)) + elif axis is 'Y': + sha.skew(0, num, point=(xminimal, yminimal)) + self.draw_app.plot_all() + + # self.draw_app.add_shape(DrawToolShape(sha.geo)) + # + # self.draw_app.transform_complete.emit() + + self.app.inform.emit(_('[success] Skew on the %s axis done ...') % str(axis)) + self.app.progress.emit(100) + + except Exception as e: + self.app.inform.emit(_("[ERROR_NOTCL] Due of %s, Skew action was not executed.") % str(e)) + return + + def on_scale(self, axis, xfactor, yfactor, point=None): + shape_list = self.draw_app.selected + xminlist = [] + yminlist = [] + xmaxlist = [] + ymaxlist = [] + + if not shape_list: + self.app.inform.emit(_("[WARNING_NOTCL] No shape selected. Please Select a shape to scale!")) + return + else: + with self.app.proc_container.new(_("Applying Scale")): + try: + # first get a bounding box to fit all + for sha in shape_list: + xmin, ymin, xmax, ymax = sha.bounds() + xminlist.append(xmin) + yminlist.append(ymin) + xmaxlist.append(xmax) + ymaxlist.append(ymax) + + # get the minimum x,y and maximum x,y for all objects selected + xminimal = min(xminlist) + yminimal = min(yminlist) + xmaximal = max(xmaxlist) + ymaximal = max(ymaxlist) + + self.app.progress.emit(20) + + if point is None: + px = 0.5 * (xminimal + xmaximal) + py = 0.5 * (yminimal + ymaximal) + else: + px = 0 + py = 0 + + for sha in shape_list: + sha.scale(xfactor, yfactor, point=(px, py)) + self.draw_app.plot_all() + + # self.draw_app.add_shape(DrawToolShape(sha.geo)) + # + # self.draw_app.transform_complete.emit() + + self.app.inform.emit(_('[success] Scale on the %s axis done ...') % str(axis)) + self.app.progress.emit(100) + except Exception as e: + self.app.inform.emit(_("[ERROR_NOTCL] Due of %s, Scale action was not executed.") % str(e)) + return + + def on_offset(self, axis, num): + shape_list = self.draw_app.selected + xminlist = [] + yminlist = [] + + if not shape_list: + self.app.inform.emit(_("[WARNING_NOTCL] No shape selected. Please Select a shape to offset!")) + return + else: + with self.app.proc_container.new(_("Applying Offset")): + try: + # first get a bounding box to fit all + for sha in shape_list: + xmin, ymin, xmax, ymax = sha.bounds() + xminlist.append(xmin) + yminlist.append(ymin) + + # get the minimum x,y and maximum x,y for all objects selected + xminimal = min(xminlist) + yminimal = min(yminlist) + self.app.progress.emit(20) + + for sha in shape_list: + if axis is 'X': + sha.offset((num, 0)) + elif axis is 'Y': + sha.offset((0, num)) + self.draw_app.plot_all() + + # self.draw_app.add_shape(DrawToolShape(sha.geo)) + # + # self.draw_app.transform_complete.emit() + + self.app.inform.emit(_('[success] Offset on the %s axis done ...') % str(axis)) + self.app.progress.emit(100) + + except Exception as e: + self.app.inform.emit(_("[ERROR_NOTCL] Due of %s, Offset action was not executed.") % str(e)) + return + + def on_rotate_key(self): + val_box = FCInputDialog(title=_("Rotate ..."), + text=_('Enter an Angle Value (degrees):'), + min=-359.9999, max=360.0000, decimals=4, + init_val=float(self.app.defaults['tools_transform_rotate'])) + val_box.setWindowIcon(QtGui.QIcon('share/rotate.png')) + + val, ok = val_box.get_value() + if ok: + self.on_rotate(val=val) + self.app.inform.emit( + _("[success] Geometry shape rotate done...") + ) + return + else: + self.app.inform.emit( + _("[WARNING_NOTCL] Geometry shape rotate cancelled...") + ) + + def on_offx_key(self): + units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower() + + val_box = FCInputDialog(title=_("Offset on X axis ..."), + text=(_('Enter a distance Value (%s):') % str(units)), + min=-9999.9999, max=10000.0000, decimals=4, + init_val=float(self.app.defaults['tools_transform_offset_x'])) + val_box.setWindowIcon(QtGui.QIcon('share/offsetx32.png')) + + val, ok = val_box.get_value() + if ok: + self.on_offx(val=val) + self.app.inform.emit( + _("[success] Geometry shape offset on X axis done...")) + return + else: + self.app.inform.emit( + _("[WARNING_NOTCL] Geometry shape offset X cancelled...")) + + def on_offy_key(self): + units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower() + + val_box = FCInputDialog(title=_("Offset on Y axis ..."), + text=(_('Enter a distance Value (%s):') % str(units)), + min=-9999.9999, max=10000.0000, decimals=4, + init_val=float(self.app.defaults['tools_transform_offset_y'])) + val_box.setWindowIcon(QtGui.QIcon('share/offsety32.png')) + + val, ok = val_box.get_value() + if ok: + self.on_offx(val=val) + self.app.inform.emit( + _("[success] Geometry shape offset on Y axis done...")) + return + else: + self.app.inform.emit( + _("[WARNING_NOTCL] Geometry shape offset Y cancelled...")) + + def on_skewx_key(self): + val_box = FCInputDialog(title=_("Skew on X axis ..."), + text=_('Enter an Angle Value (degrees):'), + min=-359.9999, max=360.0000, decimals=4, + init_val=float(self.app.defaults['tools_transform_skew_x'])) + val_box.setWindowIcon(QtGui.QIcon('share/skewX.png')) + + val, ok = val_box.get_value() + if ok: + self.on_skewx(val=val) + self.app.inform.emit( + _("[success] Geometry shape skew on X axis done...")) + return + else: + self.app.inform.emit( + _("[WARNING_NOTCL] Geometry shape skew X cancelled...")) + + def on_skewy_key(self): + val_box = FCInputDialog(title=_("Skew on Y axis ..."), + text=_('Enter an Angle Value (degrees):'), + min=-359.9999, max=360.0000, decimals=4, + init_val=float(self.app.defaults['tools_transform_skew_y'])) + val_box.setWindowIcon(QtGui.QIcon('share/skewY.png')) + + val, ok = val_box.get_value() + if ok: + self.on_skewx(val=val) + self.app.inform.emit( + _("[success] Geometry shape skew on Y axis done...")) + return + else: + self.app.inform.emit( + _("[WARNING_NOTCL] Geometry shape skew Y cancelled...")) diff --git a/flatcamGUI/FlatCAMGUI.py b/flatcamGUI/FlatCAMGUI.py index fd4b23d3..d1ab188c 100644 --- a/flatcamGUI/FlatCAMGUI.py +++ b/flatcamGUI/FlatCAMGUI.py @@ -472,6 +472,9 @@ class FlatCAMGUI(QtWidgets.QMainWindow): _('Buffer\tB')) self.grb_add_scale_menuitem = self.grb_editor_menu.addAction(QtGui.QIcon('share/scale32.png'), _('Scale\tS')) + self.grb_transform_menuitem = self.grb_editor_menu.addAction( + QtGui.QIcon('share/transform.png'),_( "Transform\tALT+R") + ) self.grb_editor_menu.addSeparator() self.grb_copy_menuitem = self.grb_editor_menu.addAction(QtGui.QIcon('share/copy32.png'), _('Copy\tC')) @@ -692,6 +695,8 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.aperture_copy_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/copy32.png'), _("Copy")) self.aperture_delete_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/trash32.png'), _("Delete")) + self.grb_transform_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/transform.png'), + _("Transformations")) self.grb_edit_toolbar.addSeparator() self.aperture_move_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/move32.png'), _("Move")) @@ -1784,6 +1789,8 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.aperture_copy_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/copy32.png'), _("Copy")) self.aperture_delete_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/trash32.png'), _("Delete")) + self.grb_transform_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/transform.png'), + _("Transformations")) self.grb_edit_toolbar.addSeparator() self.aperture_move_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/move32.png'), _("Move")) @@ -2268,7 +2275,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.app.geo_editor.delete_selected() self.app.geo_editor.replot() - # Move + # Rotate if key == QtCore.Qt.Key_Space or key == 'Space': self.app.geo_editor.transform_tool.on_rotate_key() @@ -2484,6 +2491,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.app.on_toggle_notebook() return + # Rotate + if key == QtCore.Qt.Key_Space or key == 'Space': + self.app.grb_editor.transform_tool.on_rotate_key() + # Switch to Project Tab if key == QtCore.Qt.Key_1 or key == '1': self.app.grb_editor.launched_from_shortcuts = True @@ -2515,7 +2526,6 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.app.inform.emit(_("[WARNING_NOTCL] Cancelled. Nothing selected to copy.")) return - # Scale Tool if key == QtCore.Qt.Key_B or key == 'B': self.app.grb_editor.launched_from_shortcuts = True