diff --git a/CHANGELOG.md b/CHANGELOG.md index 0def0bae..933167bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ CHANGELOG for FlatCAM Evo beta - solved a ZeroDivisionError exception in the Geometry Editor -> Path Tool - in Geometry Editor, for Path tool, added the ability to differentiate between creating a multidigit number for the projection and starting a new number (for a new segment) - added ability to control the color of the project items for the dark theme too +- in Geometry Editor, for the Polygon Tool added the UI, the length projection and the cursor data, just like for the Path Tool 13.04.2022 diff --git a/appEditors/AppGeoEditor.py b/appEditors/AppGeoEditor.py index f41c2c27..24e7231e 100644 --- a/appEditors/AppGeoEditor.py +++ b/appEditors/AppGeoEditor.py @@ -354,7 +354,7 @@ class DrawToolShape(object): class DrawToolUtilityShape(DrawToolShape): """ Utility shapes are temporary geometry in the editor - to assist in the creation of shapes. For example it + to assist in the creation of shapes. For example, it will show the outline of a rectangle from the first point to the current mouse pointer before the second point is clicked and the final geometry is created. @@ -848,6 +848,8 @@ class FCPolygon(FCShapeTool): DrawTool.__init__(self, draw_app) self.name = 'polygon' self.draw_app = draw_app + self.app = self.draw_app.app + self.plugin_name = _("Polygon") try: QtGui.QGuiApplication.restoreOverrideCursor() @@ -856,8 +858,229 @@ class FCPolygon(FCShapeTool): self.cursor = QtGui.QCursor(QtGui.QPixmap(self.draw_app.app.resource_location + '/aero.png')) QtGui.QGuiApplication.setOverrideCursor(self.cursor) + if self.app.use_3d_engine: + self.draw_app.app.plotcanvas.view.camera.zoom_callback = self.draw_cursor_data self.draw_app.app.jump_signal.connect(lambda x: self.draw_app.update_utility_geometry(data=x)) + self.polygon_tool = PathEditorTool(self.app, self.draw_app, plugin_name=self.plugin_name) + self.polygon_tool.run() + self.new_segment = True + + self.app.ui.notebook.setTabText(2, self.plugin_name) + if self.draw_app.app.ui.splitter.sizes()[0] == 0: + self.draw_app.app.ui.splitter.setSizes([1, 1]) + + self.draw_app.app.inform.emit(_("Click on 1st corner ...")) + + def click(self, point): + try: + self.draw_app.app.jump_signal.disconnect() + except (TypeError, AttributeError): + pass + self.draw_app.app.jump_signal.connect(lambda x: self.draw_app.update_utility_geometry(data=x)) + + self.draw_app.in_action = True + if point != self.points[-1:]: + self.points.append(point) + + if len(self.points) > 0: + self.draw_app.app.inform.emit(_("Click on next Point or click right mouse button to complete ...")) + return "Click on next point or hit ENTER to complete ..." + + return "" + + def make(self): + try: + QtGui.QGuiApplication.restoreOverrideCursor() + except Exception: + pass + + if self.points[-1] == self.points[-2]: + self.points.pop(-1) + + # self.geometry = LinearRing(self.points) + self.geometry = DrawToolShape(LinearRing(self.points)) + self.draw_app.in_action = False + self.complete = True + + self.draw_app.app.jump_signal.disconnect() + self.geometry.data['type'] = self.plugin_name + self.draw_cursor_data(delete=True) + self.draw_app.app.inform.emit('[success] %s' % _("Done.")) + + def utility_geometry(self, data=None): + if len(self.points) == 1: + temp_points = [x for x in self.points] + temp_points.append(data) + return DrawToolUtilityShape(LineString(temp_points)) + elif len(self.points) > 1: + temp_points = [x for x in self.points] + temp_points.append(data) + return DrawToolUtilityShape(LinearRing(temp_points)) + else: + return None + + def draw_cursor_data(self, pos=None, delete=False): + if pos is None: + pos = self.draw_app.snap_x, self.draw_app.snap_y + + if delete: + if self.draw_app.app.use_3d_engine: + self.draw_app.app.plotcanvas.text_cursor.parent = None + self.draw_app.app.plotcanvas.view.camera.zoom_callback = lambda *args: None + return + + # font size + qsettings = QtCore.QSettings("Open Source", "FlatCAM") + if qsettings.contains("hud_font_size"): + fsize = qsettings.value('hud_font_size', type=int) + else: + fsize = 8 + + x = pos[0] + y = pos[1] + try: + length = abs(np.sqrt((pos[0] - self.points[-1][0]) ** 2 + (pos[1] - self.points[-1][1]) ** 2)) + except IndexError: + length = self.draw_app.app.dec_format(0.0, self.draw_app.app.decimals) + units = self.draw_app.app.app_units.lower() + + x_dec = str(self.draw_app.app.dec_format(x, self.draw_app.app.decimals)) if x else '0.0' + y_dec = str(self.draw_app.app.dec_format(y, self.draw_app.app.decimals)) if y else '0.0' + length_dec = str(self.draw_app.app.dec_format(length, self.draw_app.app.decimals)) if length else '0.0' + + l1_txt = 'X: %s [%s]' % (x_dec, units) + l2_txt = 'Y: %s [%s]' % (y_dec, units) + l3_txt = 'L: %s [%s]' % (length_dec, units) + cursor_text = '%s\n%s\n\n%s' % (l1_txt, l2_txt, l3_txt) + + if self.draw_app.app.use_3d_engine: + new_pos = self.draw_app.app.plotcanvas.translate_coords_2((x, y)) + x, y, __, ___ = self.draw_app.app.plotcanvas.translate_coords((new_pos[0]+30, new_pos[1])) + + # text + self.draw_app.app.plotcanvas.text_cursor.font_size = fsize + self.draw_app.app.plotcanvas.text_cursor.text = cursor_text + self.draw_app.app.plotcanvas.text_cursor.pos = x, y + self.draw_app.app.plotcanvas.text_cursor.anchors = 'left', 'top' + + if self.draw_app.app.plotcanvas.text_cursor.parent is None: + self.draw_app.app.plotcanvas.text_cursor.parent = self.draw_app.app.plotcanvas.view.scene + + def on_key(self, key): + # Jump to coords + if key == QtCore.Qt.Key.Key_J or key == 'J': + self.draw_app.app.on_jump_to() + + if key == 'Backspace' or key == QtCore.Qt.Key.Key_Backspace: + if len(self.points) > 0: + self.points = self.points[0:-1] + # Remove any previous utility shape + self.draw_app.tool_shape.clear(update=False) + geo = self.utility_geometry(data=(self.draw_app.snap_x, self.draw_app.snap_y)) + self.draw_app.draw_utility_geometry(geo=geo) + if geo: + return _("Backtracked one point ...") + else: + self.draw_app.app.inform.emit(_("Click on 1st corner ...")) + + if key in [str(i) for i in range(10)] + ['.', ',', '+', '-', '/', '*'] or \ + key in [QtCore.Qt.Key.Key_0, QtCore.Qt.Key.Key_0, QtCore.Qt.Key.Key_1, QtCore.Qt.Key.Key_2, + QtCore.Qt.Key.Key_3, QtCore.Qt.Key.Key_4, QtCore.Qt.Key.Key_5, QtCore.Qt.Key.Key_6, + QtCore.Qt.Key.Key_7, QtCore.Qt.Key.Key_8, QtCore.Qt.Key.Key_9, QtCore.Qt.Key.Key_Minus, + QtCore.Qt.Key.Key_Plus, QtCore.Qt.Key.Key_Comma, QtCore.Qt.Key.Key_Period, + QtCore.Qt.Key.Key_Slash, QtCore.Qt.Key.Key_Asterisk]: + try: + # VisPy keys + if self.polygon_tool.length == 0 or self.new_segment is True: + self.polygon_tool.length = str(key.name) + self.new_segment = False + else: + self.polygon_tool.length = str(self.polygon_tool.length) + str(key.name) + except AttributeError: + # Qt keys + if self.polygon_tool.length == 0 or self.new_segment is True: + self.polygon_tool.length = chr(key) + self.new_segment = False + else: + self.polygon_tool.length = str(self.polygon_tool.length) + chr(key) + + if key == 'Enter' or key == QtCore.Qt.Key.Key_Return or key == QtCore.Qt.Key.Key_Enter: + if self.polygon_tool.length != 0: + target_length = self.polygon_tool.length + if target_length is None: + self.polygon_tool.length = 0.0 + return _("Failed.") + + first_pt = self.points[-1] + last_pt = self.draw_app.app.mouse + + seg_length = math.sqrt((last_pt[0] - first_pt[0])**2 + (last_pt[1] - first_pt[1])**2) + if seg_length == 0.0: + return + try: + new_x = first_pt[0] + (last_pt[0] - first_pt[0]) / seg_length * target_length + new_y = first_pt[1] + (last_pt[1] - first_pt[1]) / seg_length * target_length + except ZeroDivisionError as err: + self.points = [] + self.clean_up() + return '[ERROR_NOTCL] %s %s' % (_("Failed."), str(err).capitalize()) + + if self.points[-1] != (new_x, new_y): + self.points.append((new_x, new_y)) + self.new_segment = True + self.draw_app.app.on_jump_to(custom_location=(new_x, new_y), fit_center=False) + if len(self.points) > 0: + msg = '%s: %s. %s' % ( + _("Projected"), str(self.polygon_tool.length), + _("Click on next Point or click right mouse button to complete ...")) + self.draw_app.app.inform.emit(msg) + + def clean_up(self): + self.draw_app.selected = [] + self.draw_app.plot_all() + + if self.draw_app.app.use_3d_engine: + self.draw_app.app.plotcanvas.text_cursor.parent = None + self.draw_app.app.plotcanvas.view.camera.zoom_callback = lambda *args: None + + try: + self.draw_app.app.jump_signal.disconnect() + except (TypeError, AttributeError): + pass + self.polygon_tool.on_tab_close() + + +class FCPath(FCShapeTool): + """ + Resulting type: LineString + """ + + def __init__(self, draw_app): + FCShapeTool.__init__(self, draw_app) + self.draw_app = draw_app + self.name = 'path' + self.app = self.draw_app.app + + try: + QtGui.QGuiApplication.restoreOverrideCursor() + except Exception: + pass + self.cursor = QtGui.QCursor(QtGui.QPixmap(self.draw_app.app.resource_location + '/aero_path5.png')) + QtGui.QGuiApplication.setOverrideCursor(self.cursor) + + if self.app.use_3d_engine: + self.draw_app.app.plotcanvas.view.camera.zoom_callback = self.draw_cursor_data + self.draw_app.app.jump_signal.connect(lambda x: self.draw_app.update_utility_geometry(data=x)) + + self.path_tool = PathEditorTool(self.app, self.draw_app, plugin_name=_("Path")) + self.path_tool.run() + self.new_segment = True + + self.app.ui.notebook.setTabText(2, _("Path")) + if self.draw_app.app.ui.splitter.sizes()[0] == 0: + self.draw_app.app.ui.splitter.setSizes([1, 1]) + self.draw_app.app.inform.emit(_("Click on 1st point ...")) def click(self, point): @@ -877,90 +1100,6 @@ class FCPolygon(FCShapeTool): return "" - def utility_geometry(self, data=None): - if len(self.points) == 1: - temp_points = [x for x in self.points] - temp_points.append(data) - return DrawToolUtilityShape(LineString(temp_points)) - - if len(self.points) > 1: - temp_points = [x for x in self.points] - temp_points.append(data) - return DrawToolUtilityShape(LinearRing(temp_points)) - - return None - - def make(self): - try: - QtGui.QGuiApplication.restoreOverrideCursor() - except Exception: - pass - - if self.points[-1] == self.points[-2]: - self.points.pop(-1) - - # self.geometry = LinearRing(self.points) - self.geometry = DrawToolShape(LinearRing(self.points)) - self.draw_app.in_action = False - self.complete = True - - self.draw_app.app.jump_signal.disconnect() - self.geometry.data['type'] = _('Polygon') - self.draw_app.app.inform.emit('[success] %s' % _("Done.")) - - def on_key(self, key): - # Jump to coords - if key == QtCore.Qt.Key.Key_J or key == 'J': - self.draw_app.app.on_jump_to() - - if key == 'Backspace' or key == QtCore.Qt.Key.Key_Backspace: - if len(self.points) > 0: - self.points = self.points[0:-1] - # Remove any previous utility shape - self.draw_app.tool_shape.clear(update=False) - geo = self.utility_geometry(data=(self.draw_app.snap_x, self.draw_app.snap_y)) - self.draw_app.draw_utility_geometry(geo=geo) - return _("Backtracked one point ...") - - def clean_up(self): - self.draw_app.selected = [] - self.draw_app.plot_all() - - try: - self.draw_app.app.jump_signal.disconnect() - except (TypeError, AttributeError): - pass - - -class FCPath(FCPolygon): - """ - Resulting type: LineString - """ - - def __init__(self, draw_app): - FCPolygon.__init__(self, draw_app) - self.draw_app = draw_app - self.name = 'path' - self.app = self.draw_app.app - - try: - QtGui.QGuiApplication.restoreOverrideCursor() - except Exception: - pass - self.cursor = QtGui.QCursor(QtGui.QPixmap(self.draw_app.app.resource_location + '/aero_path5.png')) - QtGui.QGuiApplication.setOverrideCursor(self.cursor) - - self.draw_app.app.plotcanvas.view.camera.zoom_callback = self.draw_cursor_data - self.draw_app.app.jump_signal.connect(lambda x: self.draw_app.update_utility_geometry(data=x)) - - self.path_tool = PathEditorTool(self.app, self.draw_app) - self.path_tool.run() - self.new_segment = True - - self.app.ui.notebook.setTabText(2, _("Path")) - if self.draw_app.app.ui.splitter.sizes()[0] == 0: - self.draw_app.app.ui.splitter.setSizes([1, 1]) - def make(self): self.geometry = DrawToolShape(LineString(self.points)) self.name = 'path' @@ -1114,7 +1253,6 @@ class FCPath(FCPolygon): def clean_up(self): self.draw_app.selected = [] - self.interpolate_length = '' self.draw_app.plot_all() if self.draw_app.app.use_3d_engine: @@ -3413,7 +3551,7 @@ class AppGeoEditor(QtCore.QObject): pass else: self.update_utility_geometry(data=(x, y)) - if self.active_tool.name == 'path': + if self.active_tool.name in ['path', 'polygon']: self.active_tool.draw_cursor_data(pos=(x, y)) # ### Selection area on canvas section ### diff --git a/appEditors/plugins/GeoPathPlugin.py b/appEditors/plugins/GeoPathPlugin.py index 60206662..6c9dc6f6 100644 --- a/appEditors/plugins/GeoPathPlugin.py +++ b/appEditors/plugins/GeoPathPlugin.py @@ -11,13 +11,14 @@ class PathEditorTool(AppTool): Simple input for buffer distance. """ - def __init__(self, app, draw_app): + def __init__(self, app, draw_app, plugin_name): AppTool.__init__(self, app) self.draw_app = draw_app self.decimals = app.decimals + self.plugin_name = plugin_name - self.ui = PathEditorUI(layout=self.layout, path_class=self) + self.ui = PathEditorUI(layout=self.layout, path_class=self, plugin_name=plugin_name) self.connect_signals_at_init() self.set_tool_ui() @@ -66,7 +67,7 @@ class PathEditorTool(AppTool): # self.app.ui.notebook.callback_on_close = self.on_tab_close - self.app.ui.notebook.setTabText(2, _("Path")) + self.app.ui.notebook.setTabText(2, self.plugin_name) def set_tool_ui(self): # Init appGUI @@ -96,9 +97,9 @@ class PathEditorTool(AppTool): class PathEditorUI: - pluginName = _("Path") - def __init__(self, layout, path_class): + def __init__(self, layout, path_class, plugin_name): + self.pluginName = plugin_name self.path_class = path_class self.decimals = self.path_class.app.decimals self.layout = layout @@ -128,6 +129,9 @@ class PathEditorUI: # Project distance self.project_line_lbl = FCLabel('%s:' % _("Length")) + self.project_line_lbl.setToolTip( + _("Length of the current segment.") + ) self.project_line_entry = NumericalEvalEntry(border_color='#0069A9') grid_path.addWidget(self.project_line_lbl, 0, 0) grid_path.addWidget(self.project_line_entry, 0, 1) diff --git a/appGUI/MainGUI.py b/appGUI/MainGUI.py index 44f80bc3..8eb9c866 100644 --- a/appGUI/MainGUI.py +++ b/appGUI/MainGUI.py @@ -3486,6 +3486,9 @@ class MainGUI(QtWidgets.QMainWindow): if self.app.geo_editor.active_tool.name == 'path' and \ self.app.geo_editor.active_tool.path_tool.length != 0.0: pass + elif self.app.geo_editor.active_tool.name == 'polygon' and \ + self.app.geo_editor.active_tool.polygon_tool.length != 0.0: + pass else: self.app.geo_editor.active_tool.click( self.app.geo_editor.snap(self.app.geo_editor.x, self.app.geo_editor.y))