- in Geometry Editor, for the Polygon Tool added the UI, the length projection and the cursor data, just like for the Path Tool

This commit is contained in:
Marius Stanciu
2022-04-14 22:00:24 +03:00
committed by Marius
parent 377d79d413
commit 6bab9be25a
4 changed files with 238 additions and 92 deletions

View File

@@ -16,6 +16,7 @@ CHANGELOG for FlatCAM Evo beta
- solved a ZeroDivisionError exception in the Geometry Editor -> Path Tool - 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) - 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 - 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 13.04.2022

View File

@@ -354,7 +354,7 @@ class DrawToolShape(object):
class DrawToolUtilityShape(DrawToolShape): class DrawToolUtilityShape(DrawToolShape):
""" """
Utility shapes are temporary geometry in the editor 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 will show the outline of a rectangle from the first
point to the current mouse pointer before the second point to the current mouse pointer before the second
point is clicked and the final geometry is created. point is clicked and the final geometry is created.
@@ -848,6 +848,8 @@ class FCPolygon(FCShapeTool):
DrawTool.__init__(self, draw_app) DrawTool.__init__(self, draw_app)
self.name = 'polygon' self.name = 'polygon'
self.draw_app = draw_app self.draw_app = draw_app
self.app = self.draw_app.app
self.plugin_name = _("Polygon")
try: try:
QtGui.QGuiApplication.restoreOverrideCursor() QtGui.QGuiApplication.restoreOverrideCursor()
@@ -856,8 +858,229 @@ class FCPolygon(FCShapeTool):
self.cursor = QtGui.QCursor(QtGui.QPixmap(self.draw_app.app.resource_location + '/aero.png')) self.cursor = QtGui.QCursor(QtGui.QPixmap(self.draw_app.app.resource_location + '/aero.png'))
QtGui.QGuiApplication.setOverrideCursor(self.cursor) 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.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 ...")) self.draw_app.app.inform.emit(_("Click on 1st point ..."))
def click(self, point): def click(self, point):
@@ -877,90 +1100,6 @@ class FCPolygon(FCShapeTool):
return "" 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): def make(self):
self.geometry = DrawToolShape(LineString(self.points)) self.geometry = DrawToolShape(LineString(self.points))
self.name = 'path' self.name = 'path'
@@ -1114,7 +1253,6 @@ class FCPath(FCPolygon):
def clean_up(self): def clean_up(self):
self.draw_app.selected = [] self.draw_app.selected = []
self.interpolate_length = ''
self.draw_app.plot_all() self.draw_app.plot_all()
if self.draw_app.app.use_3d_engine: if self.draw_app.app.use_3d_engine:
@@ -3413,7 +3551,7 @@ class AppGeoEditor(QtCore.QObject):
pass pass
else: else:
self.update_utility_geometry(data=(x, y)) 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)) self.active_tool.draw_cursor_data(pos=(x, y))
# ### Selection area on canvas section ### # ### Selection area on canvas section ###

View File

@@ -11,13 +11,14 @@ class PathEditorTool(AppTool):
Simple input for buffer distance. Simple input for buffer distance.
""" """
def __init__(self, app, draw_app): def __init__(self, app, draw_app, plugin_name):
AppTool.__init__(self, app) AppTool.__init__(self, app)
self.draw_app = draw_app self.draw_app = draw_app
self.decimals = app.decimals 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.connect_signals_at_init()
self.set_tool_ui() 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.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): def set_tool_ui(self):
# Init appGUI # Init appGUI
@@ -96,9 +97,9 @@ class PathEditorTool(AppTool):
class PathEditorUI: 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.path_class = path_class
self.decimals = self.path_class.app.decimals self.decimals = self.path_class.app.decimals
self.layout = layout self.layout = layout
@@ -128,6 +129,9 @@ class PathEditorUI:
# Project distance # Project distance
self.project_line_lbl = FCLabel('%s:' % _("Length")) 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') self.project_line_entry = NumericalEvalEntry(border_color='#0069A9')
grid_path.addWidget(self.project_line_lbl, 0, 0) grid_path.addWidget(self.project_line_lbl, 0, 0)
grid_path.addWidget(self.project_line_entry, 0, 1) grid_path.addWidget(self.project_line_entry, 0, 1)

View File

@@ -3486,6 +3486,9 @@ class MainGUI(QtWidgets.QMainWindow):
if self.app.geo_editor.active_tool.name == 'path' and \ if self.app.geo_editor.active_tool.name == 'path' and \
self.app.geo_editor.active_tool.path_tool.length != 0.0: self.app.geo_editor.active_tool.path_tool.length != 0.0:
pass pass
elif self.app.geo_editor.active_tool.name == 'polygon' and \
self.app.geo_editor.active_tool.polygon_tool.length != 0.0:
pass
else: else:
self.app.geo_editor.active_tool.click( self.app.geo_editor.active_tool.click(
self.app.geo_editor.snap(self.app.geo_editor.x, self.app.geo_editor.y)) self.app.geo_editor.snap(self.app.geo_editor.x, self.app.geo_editor.y))