diff --git a/CHANGELOG.md b/CHANGELOG.md
index 23de14c1..6820a647 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,8 @@ CHANGELOG for FlatCAM Evo beta
- fixed the display of lines in Distance Plugin when using 'snap to' together with 'multipoint'
- in Geometry Editor - update (some reformatting and adding shape data)
+- in Geometry Editor - fixed the Explode tool to work on the result of adding Text geometry
+- all the Geometry Editor plugins are moved inside another folder and the UI's are moved into their own class
7.04.2022
diff --git a/appEditors/AppGeoEditor.py b/appEditors/AppGeoEditor.py
index 52acbda1..95b7894d 100644
--- a/appEditors/AppGeoEditor.py
+++ b/appEditors/AppGeoEditor.py
@@ -1141,7 +1141,11 @@ class FCExplode(FCShapeTool):
if geo.geom_type == 'MultiLineString':
lines = [line for line in geo.geoms]
-
+ elif geo.geom_type == 'MultiPolygon':
+ lines = []
+ for poly in geo.geoms:
+ lines.append(poly.exterior)
+ lines += list(poly.interiors)
elif geo.is_ring:
geo = Polygon(geo)
ext_coords = list(geo.exterior.coords)
@@ -3466,7 +3470,8 @@ class AppGeoEditor(QtCore.QObject):
geometry = self.active_tool.geometry
try:
- for geo in geometry:
+ w_geo = geometry.geoms if isinstance(geometry, (MultiPolygon, MultiLineString)) else geometry
+ for geo in w_geo:
plot_elements += self.plot_shape(geometry=geo, color=color, linewidth=linewidth)
# Non-iterable
except TypeError:
diff --git a/appEditors/AppGerberEditor.py b/appEditors/AppGerberEditor.py
index ff69093a..b34fb812 100644
--- a/appEditors/AppGerberEditor.py
+++ b/appEditors/AppGerberEditor.py
@@ -6767,11 +6767,11 @@ class TransformEditorTool(AppTool):
Inputs to specify how to paint the selected polygons.
"""
- pluginName = _("Transform Tool")
+ pluginName = _("Transformation")
rotateName = _("Rotate")
skewName = _("Skew/Shear")
scaleName = _("Scale")
- flipName = _("Mirror (Flip)")
+ flipName = _("Mirror")
offsetName = _("Offset")
bufferName = _("Buffer")
@@ -7217,7 +7217,7 @@ class TransformEditorTool(AppTool):
AppTool.run(self)
self.set_tool_ui()
- self.app.ui.notebook.setTabText(2, _("Transform Tool"))
+ self.app.ui.notebook.setTabText(2, _("Transformation"))
def install(self, icon=None, separator=None, **kwargs):
AppTool.install(self, icon, separator, shortcut='Alt+T', **kwargs)
diff --git a/appEditors/plugins/GeoBufferPlugin.py b/appEditors/plugins/GeoBufferPlugin.py
index a550a35e..8d8f24a7 100644
--- a/appEditors/plugins/GeoBufferPlugin.py
+++ b/appEditors/plugins/GeoBufferPlugin.py
@@ -11,82 +11,22 @@ class BufferSelectionTool(AppTool):
Simple input for buffer distance.
"""
- pluginName = _("Buffer Selection")
-
def __init__(self, app, draw_app):
AppTool.__init__(self, app)
self.draw_app = draw_app
self.decimals = app.decimals
- # Title
- title_label = FCLabel("%s" % ('Editor ' + self.pluginName))
- title_label.setStyleSheet("""
- QLabel
- {
- font-size: 16px;
- font-weight: bold;
- }
- """)
- self.layout.addWidget(title_label)
+ self.ui = BufferEditorUI(layout=self.layout, buffer_class=self)
- # this way I can hide/show the frame
- self.buffer_tool_frame = QtWidgets.QFrame()
- self.buffer_tool_frame.setContentsMargins(0, 0, 0, 0)
- self.layout.addWidget(self.buffer_tool_frame)
- self.buffer_tools_box = QtWidgets.QVBoxLayout()
- self.buffer_tools_box.setContentsMargins(0, 0, 0, 0)
- self.buffer_tool_frame.setLayout(self.buffer_tools_box)
-
- # Grid Layout
- grid_buffer = GLay(v_spacing=5, h_spacing=3)
- self.buffer_tools_box.addLayout(grid_buffer)
-
- # Buffer distance
- self.buffer_distance_entry = FCDoubleSpinner()
- self.buffer_distance_entry.set_precision(self.decimals)
- self.buffer_distance_entry.set_range(0.0000, 9910000.0000)
- grid_buffer.addWidget(FCLabel('%s:' % _("Buffer distance")), 0, 0)
- grid_buffer.addWidget(self.buffer_distance_entry, 0, 1)
-
- self.buffer_corner_lbl = FCLabel('%s:' % _("Buffer corner"))
- self.buffer_corner_lbl.setToolTip(
- _("There are 3 types of corners:\n"
- " - 'Round': the corner is rounded for exterior buffer.\n"
- " - 'Square': the corner is met in a sharp angle for exterior buffer.\n"
- " - 'Beveled': the corner is a line that directly connects the features meeting in the corner")
- )
- self.buffer_corner_cb = FCComboBox()
- self.buffer_corner_cb.addItem(_("Round"))
- self.buffer_corner_cb.addItem(_("Square"))
- self.buffer_corner_cb.addItem(_("Beveled"))
- grid_buffer.addWidget(self.buffer_corner_lbl, 2, 0)
- grid_buffer.addWidget(self.buffer_corner_cb, 2, 1)
-
- # Buttons
- hlay = QtWidgets.QHBoxLayout()
- grid_buffer.addLayout(hlay, 4, 0, 1, 2)
-
- self.buffer_int_button = FCButton(_("Buffer Interior"))
- hlay.addWidget(self.buffer_int_button)
- self.buffer_ext_button = FCButton(_("Buffer Exterior"))
- hlay.addWidget(self.buffer_ext_button)
-
- hlay1 = QtWidgets.QHBoxLayout()
- grid_buffer.addLayout(hlay1, 6, 0, 1, 2)
-
- self.buffer_button = FCButton(_("Full Buffer"))
- hlay1.addWidget(self.buffer_button)
-
- self.layout.addStretch(1)
+ self.connect_signals_at_init()
+ self.set_tool_ui()
+ def connect_signals_at_init(self):
# Signals
- self.buffer_button.clicked.connect(self.on_buffer)
- self.buffer_int_button.clicked.connect(self.on_buffer_int)
- self.buffer_ext_button.clicked.connect(self.on_buffer_ext)
-
- # Init appGUI
- self.buffer_distance_entry.set_value(0.01)
+ self.ui.buffer_button.clicked.connect(self.on_buffer)
+ self.ui.buffer_int_button.clicked.connect(self.on_buffer_int)
+ self.ui.buffer_ext_button.clicked.connect(self.on_buffer_ext)
def run(self):
self.app.defaults.report_usage("Geo Editor ToolBuffer()")
@@ -123,59 +63,63 @@ class BufferSelectionTool(AppTool):
self.app.ui.notebook.setTabText(2, _("Buffer Tool"))
+ def set_tool_ui(self):
+ # Init appGUI
+ self.ui.buffer_distance_entry.set_value(0.01)
+
def on_tab_close(self):
self.draw_app.select_tool("select")
self.app.ui.notebook.callback_on_close = lambda: None
def on_buffer(self):
try:
- buffer_distance = float(self.buffer_distance_entry.get_value())
+ buffer_distance = float(self.ui.buffer_distance_entry.get_value())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
- buffer_distance = float(self.buffer_distance_entry.get_value().replace(',', '.'))
- self.buffer_distance_entry.set_value(buffer_distance)
+ buffer_distance = float(self.ui.buffer_distance_entry.get_value().replace(',', '.'))
+ self.ui.buffer_distance_entry.set_value(buffer_distance)
except ValueError:
self.app.inform.emit('[WARNING_NOTCL] %s' %
_("Buffer distance value is missing or wrong format. Add it and retry."))
return
# the cb index start from 0 but the join styles for the buffer start from 1 therefore the adjustment
# I populated the combobox such that the index coincide with the join styles value (which is really an INT)
- join_style = self.buffer_corner_cb.currentIndex() + 1
+ join_style = self.ui.buffer_corner_cb.currentIndex() + 1
self.buffer(buffer_distance, join_style)
def on_buffer_int(self):
try:
- buffer_distance = float(self.buffer_distance_entry.get_value())
+ buffer_distance = float(self.ui.buffer_distance_entry.get_value())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
- buffer_distance = float(self.buffer_distance_entry.get_value().replace(',', '.'))
- self.buffer_distance_entry.set_value(buffer_distance)
+ buffer_distance = float(self.ui.buffer_distance_entry.get_value().replace(',', '.'))
+ self.ui.buffer_distance_entry.set_value(buffer_distance)
except ValueError:
self.app.inform.emit('[WARNING_NOTCL] %s' %
_("Buffer distance value is missing or wrong format. Add it and retry."))
return
# the cb index start from 0 but the join styles for the buffer start from 1 therefore the adjustment
# I populated the combobox such that the index coincide with the join styles value (which is really an INT)
- join_style = self.buffer_corner_cb.currentIndex() + 1
+ join_style = self.ui.buffer_corner_cb.currentIndex() + 1
self.buffer_int(buffer_distance, join_style)
def on_buffer_ext(self):
try:
- buffer_distance = float(self.buffer_distance_entry.get_value())
+ buffer_distance = float(self.ui.buffer_distance_entry.get_value())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
- buffer_distance = float(self.buffer_distance_entry.get_value().replace(',', '.'))
- self.buffer_distance_entry.set_value(buffer_distance)
+ buffer_distance = float(self.ui.buffer_distance_entry.get_value().replace(',', '.'))
+ self.ui.buffer_distance_entry.set_value(buffer_distance)
except ValueError:
self.app.inform.emit('[WARNING_NOTCL] %s' %
_("Buffer distance value is missing or wrong format. Add it and retry."))
return
# the cb index start from 0 but the join styles for the buffer start from 1 therefore the adjustment
# I populated the combobox such that the index coincide with the join styles value (which is really an INT)
- join_style = self.buffer_corner_cb.currentIndex() + 1
+ join_style = self.ui.buffer_corner_cb.currentIndex() + 1
self.buffer_ext(buffer_distance, join_style)
def buffer(self, buf_distance, join_style):
@@ -393,5 +337,75 @@ class BufferSelectionTool(AppTool):
self.app.worker_task.emit({'fcn': work_task, 'params': [self]})
def hide_tool(self):
- self.buffer_tool_frame.hide()
+ self.ui.buffer_tool_frame.hide()
self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
+
+
+class BufferEditorUI:
+ pluginName = _("Buffer")
+
+ def __init__(self, layout, buffer_class):
+ self.buffer_class = buffer_class
+ self.decimals = self.buffer_class.app.decimals
+ self.layout = layout
+
+ # Title
+ title_label = FCLabel("%s" % ('Editor ' + self.pluginName))
+ title_label.setStyleSheet("""
+ QLabel
+ {
+ font-size: 16px;
+ font-weight: bold;
+ }
+ """)
+ self.layout.addWidget(title_label)
+
+ # this way I can hide/show the frame
+ self.buffer_tool_frame = QtWidgets.QFrame()
+ self.buffer_tool_frame.setContentsMargins(0, 0, 0, 0)
+ self.layout.addWidget(self.buffer_tool_frame)
+ self.buffer_tools_box = QtWidgets.QVBoxLayout()
+ self.buffer_tools_box.setContentsMargins(0, 0, 0, 0)
+ self.buffer_tool_frame.setLayout(self.buffer_tools_box)
+
+ # Grid Layout
+ grid_buffer = GLay(v_spacing=5, h_spacing=3)
+ self.buffer_tools_box.addLayout(grid_buffer)
+
+ # Buffer distance
+ self.buffer_distance_entry = FCDoubleSpinner()
+ self.buffer_distance_entry.set_precision(self.decimals)
+ self.buffer_distance_entry.set_range(0.0000, 9910000.0000)
+ grid_buffer.addWidget(FCLabel('%s:' % _("Buffer distance")), 0, 0)
+ grid_buffer.addWidget(self.buffer_distance_entry, 0, 1)
+
+ self.buffer_corner_lbl = FCLabel('%s:' % _("Buffer corner"))
+ self.buffer_corner_lbl.setToolTip(
+ _("There are 3 types of corners:\n"
+ " - 'Round': the corner is rounded for exterior buffer.\n"
+ " - 'Square': the corner is met in a sharp angle for exterior buffer.\n"
+ " - 'Beveled': the corner is a line that directly connects the features meeting in the corner")
+ )
+ self.buffer_corner_cb = FCComboBox()
+ self.buffer_corner_cb.addItem(_("Round"))
+ self.buffer_corner_cb.addItem(_("Square"))
+ self.buffer_corner_cb.addItem(_("Beveled"))
+ grid_buffer.addWidget(self.buffer_corner_lbl, 2, 0)
+ grid_buffer.addWidget(self.buffer_corner_cb, 2, 1)
+
+ # Buttons
+ hlay = QtWidgets.QHBoxLayout()
+ grid_buffer.addLayout(hlay, 4, 0, 1, 2)
+
+ self.buffer_int_button = FCButton(_("Buffer Interior"))
+ hlay.addWidget(self.buffer_int_button)
+ self.buffer_ext_button = FCButton(_("Buffer Exterior"))
+ hlay.addWidget(self.buffer_ext_button)
+
+ hlay1 = QtWidgets.QHBoxLayout()
+ grid_buffer.addLayout(hlay1, 6, 0, 1, 2)
+
+ self.buffer_button = FCButton(_("Full Buffer"))
+ hlay1.addWidget(self.buffer_button)
+
+ self.layout.addStretch(1)
diff --git a/appEditors/plugins/GeoPaintPlugin.py b/appEditors/plugins/GeoPaintPlugin.py
index aaddd334..a96c6f92 100644
--- a/appEditors/plugins/GeoPaintPlugin.py
+++ b/appEditors/plugins/GeoPaintPlugin.py
@@ -12,8 +12,6 @@ class PaintOptionsTool(AppTool):
Inputs to specify how to paint the selected polygons.
"""
- pluginName = _("Paint Tool")
-
def __init__(self, app, fcdraw):
AppTool.__init__(self, app)
@@ -21,6 +19,211 @@ class PaintOptionsTool(AppTool):
self.fcdraw = fcdraw
self.decimals = self.app.decimals
+ self.ui = PaintEditorUI(layout=self.layout, paint_class=self)
+
+ self.connect_signals_at_init()
+ self.set_tool_ui()
+
+ def run(self):
+ self.app.defaults.report_usage("Geo Editor ToolPaint()")
+ AppTool.run(self)
+
+ # if the splitter us hidden, display it
+ if self.app.ui.splitter.sizes()[0] == 0:
+ self.app.ui.splitter.setSizes([1, 1])
+
+ # if the Tool Tab is hidden display it, else hide it but only if the objectName is the same
+ found_idx = None
+ for idx in range(self.app.ui.notebook.count()):
+ if self.app.ui.notebook.widget(idx).objectName() == "plugin_tab":
+ found_idx = idx
+ break
+ # show the Tab
+ if not found_idx:
+ try:
+ self.app.ui.notebook.addTab(self.app.ui.plugin_tab, _("Plugin"))
+ except RuntimeError:
+ self.app.ui.plugin_tab = QtWidgets.QWidget()
+ self.app.ui.plugin_tab.setObjectName("plugin_tab")
+ self.app.ui.plugin_tab_layout = QtWidgets.QVBoxLayout(self.app.ui.plugin_tab)
+ self.app.ui.plugin_tab_layout.setContentsMargins(2, 2, 2, 2)
+
+ self.app.ui.plugin_scroll_area = VerticalScrollArea()
+ self.app.ui.plugin_tab_layout.addWidget(self.app.ui.plugin_scroll_area)
+ self.app.ui.notebook.addTab(self.app.ui.plugin_tab, _("Plugin"))
+ # focus on Tool Tab
+ self.app.ui.notebook.setCurrentWidget(self.app.ui.plugin_tab)
+
+ # self.app.ui.notebook.callback_on_close = self.on_tab_close
+
+ self.app.ui.notebook.setTabText(2, _("Paint Tool"))
+
+ def connect_signals_at_init(self):
+ # Signals
+ self.ui.paint_button.clicked.connect(self.on_paint)
+
+ def on_tab_close(self):
+ self.fcdraw.select_tool("select")
+ self.app.ui.notebook.callback_on_close = lambda: None
+
+ def set_tool_ui(self):
+ # Init appGUI
+ if self.app.options["tools_paint_tooldia"]:
+ self.ui.painttooldia_entry.set_value(self.app.options["tools_paint_tooldia"])
+ else:
+ self.ui.painttooldia_entry.set_value(0.0)
+
+ if self.app.options["tools_paint_overlap"]:
+ self.ui.paintoverlap_entry.set_value(self.app.options["tools_paint_overlap"])
+ else:
+ self.ui.paintoverlap_entry.set_value(0.0)
+
+ if self.app.options["tools_paint_offset"]:
+ self.ui.paintmargin_entry.set_value(self.app.options["tools_paint_offset"])
+ else:
+ self.ui.paintmargin_entry.set_value(0.0)
+
+ if self.app.options["tools_paint_method"]:
+ self.ui.paintmethod_combo.set_value(self.app.options["tools_paint_method"])
+ else:
+ self.ui.paintmethod_combo.set_value(_("Seed"))
+
+ if self.app.options["tools_paint_connect"]:
+ self.ui.pathconnect_cb.set_value(self.app.options["tools_paint_connect"])
+ else:
+ self.ui.pathconnect_cb.set_value(False)
+
+ if self.app.options["tools_paint_contour"]:
+ self.ui.paintcontour_cb.set_value(self.app.options["tools_paint_contour"])
+ else:
+ self.ui.paintcontour_cb.set_value(False)
+
+ def on_paint(self):
+ if not self.fcdraw.selected:
+ self.app.inform.emit('[WARNING_NOTCL] %s %s' % (_("Cancelled."), _("No shape selected.")))
+ return
+
+ tooldia = self.ui.painttooldia_entry.get_value()
+ overlap = self.ui.paintoverlap_entry.get_value() / 100.0
+ margin = self.ui.paintmargin_entry.get_value()
+
+ method = self.ui.paintmethod_combo.get_value()
+ contour = self.ui.paintcontour_cb.get_value()
+ connect = self.ui.pathconnect_cb.get_value()
+
+ self.paint(tooldia, overlap, margin, connect=connect, contour=contour, method=method)
+ self.fcdraw.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 paint(self, tooldia, overlap, margin, connect, contour, method):
+ def work_task(geo_editor):
+ with geo_editor.app.proc_container.new(_("Working...")):
+ if overlap >= 100:
+ geo_editor.app.inform.emit('[ERROR_NOTCL] %s' %
+ _("Could not do Paint. Overlap value has to be less than 100%%."))
+ return
+
+ geo_editor.paint_tooldia = tooldia
+ selected = geo_editor.get_selected()
+
+ if len(selected) == 0:
+ geo_editor.app.inform.emit('[WARNING_NOTCL] %s' % _("Nothing selected."))
+ return
+
+ for param in [tooldia, overlap, margin]:
+ if not isinstance(param, float):
+ param_name = [k for k, v in locals().items() if v is param][0]
+ geo_editor.app.inform.emit('[WARNING] %s: %s' % (_("Invalid value for"), str(param)))
+
+ results = []
+
+ def recurse(geometry, reset=True):
+ """
+ Creates a list of non-iterable linear geometry objects.
+ Results are placed in self.flat_geometry
+
+ :param geometry: Shapely type or list or list of list of such.
+ :param reset: Clears the contents of self.flat_geometry.
+ """
+
+ if geometry is None:
+ return
+
+ if reset:
+ self.flat_geo = []
+
+ # If iterable, expand recursively.
+ try:
+ for geo_el in geometry:
+ if geo_el is not None:
+ recurse(geometry=geo_el, reset=False)
+
+ # Not iterable, do the actual indexing and add.
+ except TypeError:
+ self.flat_geo.append(geometry)
+
+ return self.flat_geo
+
+ for geo in selected:
+
+ local_results = []
+ for geo_obj in recurse(geo.geo):
+ try:
+ if type(geo_obj) == Polygon:
+ poly_buf = geo_obj.buffer(-margin)
+ else:
+ poly_buf = Polygon(geo_obj).buffer(-margin)
+
+ if method == _("Seed"):
+ cp = Geometry.clear_polygon2(
+ geo_editor, polygon_to_clear=poly_buf, tooldia=tooldia,
+ steps_per_circle=geo_editor.app.options["geometry_circle_steps"],
+ overlap=overlap, contour=contour, connect=connect)
+ elif method == _("Lines"):
+ cp = Geometry.clear_polygon3(
+ geo_editor, polygon=poly_buf, tooldia=tooldia,
+ steps_per_circle=geo_editor.app.options["geometry_circle_steps"],
+ overlap=overlap, contour=contour, connect=connect)
+ else:
+ cp = Geometry.clear_polygon(
+ geo_editor, polygon=poly_buf, tooldia=tooldia,
+ steps_per_circle=geo_editor.app.options["geometry_circle_steps"],
+ overlap=overlap, contour=contour, connect=connect)
+
+ if cp is not None:
+ local_results += list(cp.get_objects())
+ except Exception as e:
+ geo_editor.app.log.error("Could not Paint the polygons. %s" % str(e))
+ geo_editor.app.inform.emit(
+ '[ERROR] %s\n%s' % (_("Could not do Paint. Try a different combination of parameters. "
+ "Or a different method of Paint"), str(e))
+ )
+ return
+
+ # add the result to the results list
+ results.append(unary_union(local_results))
+
+ # This is a dirty patch:
+ for r in results:
+ geo_editor.add_shape(r)
+ geo_editor.plot_all()
+ geo_editor.build_ui_sig.emit()
+ geo_editor.app.inform.emit('[success] %s' % _("Done."))
+
+ self.app.worker_task.emit({'fcn': work_task, 'params': [self.fcdraw]})
+
+
+class PaintEditorUI:
+ pluginName = _("Paint")
+
+ def __init__(self, layout, paint_class):
+ self.paint_class = paint_class
+ self.decimals = self.paint_class.app.decimals
+ self.layout = layout
+
# Title
title_label = FCLabel("%s" % self.pluginName)
title_label.setStyleSheet("""
@@ -129,196 +332,4 @@ class PaintOptionsTool(AppTool):
self.paint_button = FCButton(_("Paint"))
hlay.addWidget(self.paint_button)
- self.layout.addStretch()
-
- # Signals
- self.paint_button.clicked.connect(self.on_paint)
-
- self.set_tool_ui()
-
- def run(self):
- self.app.defaults.report_usage("Geo Editor ToolPaint()")
- AppTool.run(self)
-
- # if the splitter us hidden, display it
- if self.app.ui.splitter.sizes()[0] == 0:
- self.app.ui.splitter.setSizes([1, 1])
-
- # if the Tool Tab is hidden display it, else hide it but only if the objectName is the same
- found_idx = None
- for idx in range(self.app.ui.notebook.count()):
- if self.app.ui.notebook.widget(idx).objectName() == "plugin_tab":
- found_idx = idx
- break
- # show the Tab
- if not found_idx:
- try:
- self.app.ui.notebook.addTab(self.app.ui.plugin_tab, _("Plugin"))
- except RuntimeError:
- self.app.ui.plugin_tab = QtWidgets.QWidget()
- self.app.ui.plugin_tab.setObjectName("plugin_tab")
- self.app.ui.plugin_tab_layout = QtWidgets.QVBoxLayout(self.app.ui.plugin_tab)
- self.app.ui.plugin_tab_layout.setContentsMargins(2, 2, 2, 2)
-
- self.app.ui.plugin_scroll_area = VerticalScrollArea()
- self.app.ui.plugin_tab_layout.addWidget(self.app.ui.plugin_scroll_area)
- self.app.ui.notebook.addTab(self.app.ui.plugin_tab, _("Plugin"))
- # focus on Tool Tab
- self.app.ui.notebook.setCurrentWidget(self.app.ui.plugin_tab)
-
- # self.app.ui.notebook.callback_on_close = self.on_tab_close
-
- self.app.ui.notebook.setTabText(2, _("Paint Tool"))
-
- def on_tab_close(self):
- self.fcdraw.select_tool("select")
- self.app.ui.notebook.callback_on_close = lambda: None
-
- def set_tool_ui(self):
- # Init appGUI
- if self.app.options["tools_paint_tooldia"]:
- self.painttooldia_entry.set_value(self.app.options["tools_paint_tooldia"])
- else:
- self.painttooldia_entry.set_value(0.0)
-
- if self.app.options["tools_paint_overlap"]:
- self.paintoverlap_entry.set_value(self.app.options["tools_paint_overlap"])
- else:
- self.paintoverlap_entry.set_value(0.0)
-
- if self.app.options["tools_paint_offset"]:
- self.paintmargin_entry.set_value(self.app.options["tools_paint_offset"])
- else:
- self.paintmargin_entry.set_value(0.0)
-
- if self.app.options["tools_paint_method"]:
- self.paintmethod_combo.set_value(self.app.options["tools_paint_method"])
- else:
- self.paintmethod_combo.set_value(_("Seed"))
-
- if self.app.options["tools_paint_connect"]:
- self.pathconnect_cb.set_value(self.app.options["tools_paint_connect"])
- else:
- self.pathconnect_cb.set_value(False)
-
- if self.app.options["tools_paint_contour"]:
- self.paintcontour_cb.set_value(self.app.options["tools_paint_contour"])
- else:
- self.paintcontour_cb.set_value(False)
-
- def on_paint(self):
- if not self.fcdraw.selected:
- self.app.inform.emit('[WARNING_NOTCL] %s %s' % (_("Cancelled."), _("No shape selected.")))
- return
-
- tooldia = self.painttooldia_entry.get_value()
- overlap = self.paintoverlap_entry.get_value() / 100.0
- margin = self.paintmargin_entry.get_value()
-
- method = self.paintmethod_combo.get_value()
- contour = self.paintcontour_cb.get_value()
- connect = self.pathconnect_cb.get_value()
-
- self.paint(tooldia, overlap, margin, connect=connect, contour=contour, method=method)
- self.fcdraw.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 paint(self, tooldia, overlap, margin, connect, contour, method):
- def work_task(geo_editor):
- with geo_editor.app.proc_container.new(_("Working...")):
- if overlap >= 100:
- geo_editor.app.inform.emit('[ERROR_NOTCL] %s' %
- _("Could not do Paint. Overlap value has to be less than 100%%."))
- return
-
- geo_editor.paint_tooldia = tooldia
- selected = geo_editor.get_selected()
-
- if len(selected) == 0:
- geo_editor.app.inform.emit('[WARNING_NOTCL] %s' % _("Nothing selected."))
- return
-
- for param in [tooldia, overlap, margin]:
- if not isinstance(param, float):
- param_name = [k for k, v in locals().items() if v is param][0]
- geo_editor.app.inform.emit('[WARNING] %s: %s' % (_("Invalid value for"), str(param)))
-
- results = []
-
- def recurse(geometry, reset=True):
- """
- Creates a list of non-iterable linear geometry objects.
- Results are placed in self.flat_geometry
-
- :param geometry: Shapely type or list or list of list of such.
- :param reset: Clears the contents of self.flat_geometry.
- """
-
- if geometry is None:
- return
-
- if reset:
- self.flat_geo = []
-
- # If iterable, expand recursively.
- try:
- for geo_el in geometry:
- if geo_el is not None:
- recurse(geometry=geo_el, reset=False)
-
- # Not iterable, do the actual indexing and add.
- except TypeError:
- self.flat_geo.append(geometry)
-
- return self.flat_geo
-
- for geo in selected:
-
- local_results = []
- for geo_obj in recurse(geo.geo):
- try:
- if type(geo_obj) == Polygon:
- poly_buf = geo_obj.buffer(-margin)
- else:
- poly_buf = Polygon(geo_obj).buffer(-margin)
-
- if method == _("Seed"):
- cp = Geometry.clear_polygon2(
- geo_editor, polygon_to_clear=poly_buf, tooldia=tooldia,
- steps_per_circle=geo_editor.app.options["geometry_circle_steps"],
- overlap=overlap, contour=contour, connect=connect)
- elif method == _("Lines"):
- cp = Geometry.clear_polygon3(
- geo_editor, polygon=poly_buf, tooldia=tooldia,
- steps_per_circle=geo_editor.app.options["geometry_circle_steps"],
- overlap=overlap, contour=contour, connect=connect)
- else:
- cp = Geometry.clear_polygon(
- geo_editor, polygon=poly_buf, tooldia=tooldia,
- steps_per_circle=geo_editor.app.options["geometry_circle_steps"],
- overlap=overlap, contour=contour, connect=connect)
-
- if cp is not None:
- local_results += list(cp.get_objects())
- except Exception as e:
- geo_editor.app.log.error("Could not Paint the polygons. %s" % str(e))
- geo_editor.app.inform.emit(
- '[ERROR] %s\n%s' % (_("Could not do Paint. Try a different combination of parameters. "
- "Or a different method of Paint"), str(e))
- )
- return
-
- # add the result to the results list
- results.append(unary_union(local_results))
-
- # This is a dirty patch:
- for r in results:
- geo_editor.add_shape(r)
- geo_editor.plot_all()
- geo_editor.build_ui_sig.emit()
- geo_editor.app.inform.emit('[success] %s' % _("Done."))
-
- self.app.worker_task.emit({'fcn': work_task, 'params': [self.fcdraw]})
+ self.layout.addStretch(1)
diff --git a/appEditors/plugins/GeoTextPlugin.py b/appEditors/plugins/GeoTextPlugin.py
index 5a75f207..4914e90d 100644
--- a/appEditors/plugins/GeoTextPlugin.py
+++ b/appEditors/plugins/GeoTextPlugin.py
@@ -12,8 +12,6 @@ class TextInputTool(AppTool):
Simple input for buffer distance.
"""
- pluginName = _("Text Input Tool")
-
def __init__(self, app, draw_app):
AppTool.__init__(self, app)
@@ -24,30 +22,58 @@ class TextInputTool(AppTool):
self.f_parse = ParseFont(self.app)
self.f_parse.get_fonts_by_types()
+ self.font_name = None
+ self.font_bold = False
+ self.font_italic = False
- # this way I can hide/show the frame
- self.text_tool_frame = QtWidgets.QFrame()
- self.text_tool_frame.setContentsMargins(0, 0, 0, 0)
- self.layout.addWidget(self.text_tool_frame)
- self.text_tools_box = QtWidgets.QVBoxLayout()
- self.text_tools_box.setContentsMargins(0, 0, 0, 0)
- self.text_tool_frame.setLayout(self.text_tools_box)
+ self.ui = TextEditorUI(layout=self.layout, text_class=self)
- # Title
- title_label = FCLabel("%s" % self.pluginName)
- title_label.setStyleSheet("""
- QLabel
- {
- font-size: 16px;
- font-weight: bold;
- }
- """)
- self.text_tools_box.addWidget(title_label)
+ self.connect_signals_at_init()
+ self.set_tool_ui()
- # Grid Layout
- self.grid_text = GLay(v_spacing=5, h_spacing=3)
- self.text_tools_box.addLayout(self.grid_text)
+ def run(self):
+ self.app.defaults.report_usage("Geo Editor TextInputTool()")
+ AppTool.run(self)
+ # if the splitter us hidden, display it
+ if self.app.ui.splitter.sizes()[0] == 0:
+ self.app.ui.splitter.setSizes([1, 1])
+
+ # if the Tool Tab is hidden display it, else hide it but only if the objectName is the same
+ found_idx = None
+ for idx in range(self.app.ui.notebook.count()):
+ if self.app.ui.notebook.widget(idx).objectName() == "plugin_tab":
+ found_idx = idx
+ break
+ # show the Tab
+ if not found_idx:
+ try:
+ self.app.ui.notebook.addTab(self.app.ui.plugin_tab, _("Plugin"))
+ except RuntimeError:
+ self.app.ui.plugin_tab = QtWidgets.QWidget()
+ self.app.ui.plugin_tab.setObjectName("plugin_tab")
+ self.app.ui.plugin_tab_layout = QtWidgets.QVBoxLayout(self.app.ui.plugin_tab)
+ self.app.ui.plugin_tab_layout.setContentsMargins(2, 2, 2, 2)
+
+ self.app.ui.plugin_scroll_area = VerticalScrollArea()
+ self.app.ui.plugin_tab_layout.addWidget(self.app.ui.plugin_scroll_area)
+ self.app.ui.notebook.addTab(self.app.ui.plugin_tab, _("Plugin"))
+ # focus on Tool Tab
+ self.app.ui.notebook.setCurrentWidget(self.app.ui.plugin_tab)
+
+ # self.app.ui.notebook.callback_on_close = self.on_tab_close
+
+ self.app.ui.notebook.setTabText(2, _("Text Tool"))
+
+ def connect_signals_at_init(self):
+ # Signals
+ self.ui.apply_button.clicked.connect(self.on_apply_button)
+ self.ui.font_type_cb.currentFontChanged.connect(self.font_family)
+ self.ui.font_size_cb.activated.connect(self.font_size)
+ self.ui.font_bold_tb.clicked.connect(self.on_bold_button)
+ self.ui.font_italic_tb.clicked.connect(self.on_italic_button)
+
+ def set_tool_ui(self):
# Font type
if sys.platform == "win32":
f_current = QtGui.QFont("Arial")
@@ -57,16 +83,15 @@ class TextInputTool(AppTool):
f_current = QtGui.QFont("Helvetica Neue")
self.font_name = f_current.family()
-
- self.font_type_cb = QtWidgets.QFontComboBox(self)
- self.font_type_cb.setCurrentFont(f_current)
- self.grid_text.addWidget(FCLabel('%s:' % _("Font")), 0, 0)
- self.grid_text.addWidget(self.font_type_cb, 0, 1)
+ self.ui.font_type_cb.setCurrentFont(f_current)
# Flag variables to show if font is bold, italic, both or none (regular)
self.font_bold = False
self.font_italic = False
+ self.ui.text_input_entry.setCurrentFont(f_current)
+ self.ui.text_input_entry.setFontPointSize(10)
+
# # Create dictionaries with the filenames of the fonts
# # Key: Fontname
# # Value: Font File Name.ttf
@@ -104,8 +129,105 @@ class TextInputTool(AppTool):
# except WindowsError:
# pass
- # Font size
+ def on_tab_close(self):
+ self.draw_app.select_tool("select")
+ self.app.ui.notebook.callback_on_close = lambda: None
+ def on_apply_button(self):
+ font_to_geo_type = ""
+
+ if self.font_bold is True:
+ font_to_geo_type = 'bold'
+ elif self.font_italic is True:
+ font_to_geo_type = 'italic'
+ elif self.font_bold is True and self.font_italic is True:
+ font_to_geo_type = 'bi'
+ elif self.font_bold is False and self.font_italic is False:
+ font_to_geo_type = 'regular'
+
+ string_to_geo = self.ui.text_input_entry.get_value()
+ font_to_geo_size = self.ui.font_size_cb.get_value()
+
+ self.text_path = self.f_parse.font_to_geometry(char_string=string_to_geo, font_name=self.font_name,
+ font_size=font_to_geo_size,
+ font_type=font_to_geo_type,
+ units=self.app.app_units.upper())
+
+ def font_family(self, font):
+ self.ui.text_input_entry.selectAll()
+ font.setPointSize(int(self.ui.font_size_cb.get_value()))
+ self.ui.text_input_entry.setCurrentFont(font)
+ self.font_name = self.ui.font_type_cb.currentFont().family()
+
+ def font_size(self):
+ self.ui.text_input_entry.selectAll()
+ self.ui.text_input_entry.setFontPointSize(float(self.ui.font_size_cb.get_value()))
+
+ def on_bold_button(self):
+ if self.ui.font_bold_tb.isChecked():
+ self.ui.text_input_entry.selectAll()
+ self.ui.text_input_entry.setFontWeight(QtGui.QFont.Weight.Bold)
+ self.font_bold = True
+ else:
+ self.ui.text_input_entry.selectAll()
+ self.ui.text_input_entry.setFontWeight(QtGui.QFont.Weight.Normal)
+ self.font_bold = False
+
+ def on_italic_button(self):
+ if self.ui.font_italic_tb.isChecked():
+ self.ui.text_input_entry.selectAll()
+ self.ui.text_input_entry.setFontItalic(True)
+ self.font_italic = True
+ else:
+ self.ui.text_input_entry.selectAll()
+ self.ui.text_input_entry.setFontItalic(False)
+ self.font_italic = False
+
+ def hide_tool(self):
+ self.ui.text_tool_frame.hide()
+ self.app.ui.notebook.setCurrentWidget(self.app.ui.properties_tab)
+ # self.app.ui.splitter.setSizes([0, 1])
+ # self.app.ui.notebook.setTabText(2, _("Tool"))
+ self.app.ui.notebook.removeTab(2)
+
+
+class TextEditorUI:
+ pluginName = _("Text")
+
+ def __init__(self, layout, text_class):
+ self.text_class = text_class
+ self.decimals = self.text_class.app.decimals
+ self.layout = layout
+ self.app = self.text_class.app
+
+ # this way I can hide/show the frame
+ self.text_tool_frame = QtWidgets.QFrame()
+ self.text_tool_frame.setContentsMargins(0, 0, 0, 0)
+ self.layout.addWidget(self.text_tool_frame)
+ self.text_tools_box = QtWidgets.QVBoxLayout()
+ self.text_tools_box.setContentsMargins(0, 0, 0, 0)
+ self.text_tool_frame.setLayout(self.text_tools_box)
+
+ # Title
+ title_label = FCLabel("%s" % self.pluginName)
+ title_label.setStyleSheet("""
+ QLabel
+ {
+ font-size: 16px;
+ font-weight: bold;
+ }
+ """)
+ self.text_tools_box.addWidget(title_label)
+
+ # Grid Layout
+ self.grid_text = GLay(v_spacing=5, h_spacing=3)
+ self.text_tools_box.addLayout(self.grid_text)
+
+ self.font_type_cb = QtWidgets.QFontComboBox()
+ self.grid_text.addWidget(FCLabel('%s:' % _("Font")), 0, 0)
+ self.grid_text.addWidget(self.font_type_cb, 0, 1)
+
+ # Font size
hlay = QtWidgets.QHBoxLayout()
self.font_size_cb = FCComboBox(policy=False)
@@ -144,8 +266,7 @@ class TextInputTool(AppTool):
self.text_input_entry.setTabStopDistance(12)
self.text_input_entry.setMinimumHeight(200)
# self.text_input_entry.setMaximumHeight(150)
- self.text_input_entry.setCurrentFont(f_current)
- self.text_input_entry.setFontPointSize(10)
+
self.grid_text.addWidget(self.text_input_entry, 6, 0, 1, 2)
# Buttons
@@ -153,104 +274,3 @@ class TextInputTool(AppTool):
self.grid_text.addWidget(self.apply_button, 8, 0, 1, 2)
# self.layout.addStretch()
-
- # Signals
- self.apply_button.clicked.connect(self.on_apply_button)
- self.font_type_cb.currentFontChanged.connect(self.font_family)
- self.font_size_cb.activated.connect(self.font_size)
- self.font_bold_tb.clicked.connect(self.on_bold_button)
- self.font_italic_tb.clicked.connect(self.on_italic_button)
-
- def run(self):
- self.app.defaults.report_usage("Geo Editor TextInputTool()")
- AppTool.run(self)
-
- # if the splitter us hidden, display it
- if self.app.ui.splitter.sizes()[0] == 0:
- self.app.ui.splitter.setSizes([1, 1])
-
- # if the Tool Tab is hidden display it, else hide it but only if the objectName is the same
- found_idx = None
- for idx in range(self.app.ui.notebook.count()):
- if self.app.ui.notebook.widget(idx).objectName() == "plugin_tab":
- found_idx = idx
- break
- # show the Tab
- if not found_idx:
- try:
- self.app.ui.notebook.addTab(self.app.ui.plugin_tab, _("Plugin"))
- except RuntimeError:
- self.app.ui.plugin_tab = QtWidgets.QWidget()
- self.app.ui.plugin_tab.setObjectName("plugin_tab")
- self.app.ui.plugin_tab_layout = QtWidgets.QVBoxLayout(self.app.ui.plugin_tab)
- self.app.ui.plugin_tab_layout.setContentsMargins(2, 2, 2, 2)
-
- self.app.ui.plugin_scroll_area = VerticalScrollArea()
- self.app.ui.plugin_tab_layout.addWidget(self.app.ui.plugin_scroll_area)
- self.app.ui.notebook.addTab(self.app.ui.plugin_tab, _("Plugin"))
- # focus on Tool Tab
- self.app.ui.notebook.setCurrentWidget(self.app.ui.plugin_tab)
-
- # self.app.ui.notebook.callback_on_close = self.on_tab_close
-
- self.app.ui.notebook.setTabText(2, _("Text Tool"))
-
- def on_tab_close(self):
- self.draw_app.select_tool("select")
- self.app.ui.notebook.callback_on_close = lambda: None
-
- def on_apply_button(self):
- font_to_geo_type = ""
-
- if self.font_bold is True:
- font_to_geo_type = 'bold'
- elif self.font_italic is True:
- font_to_geo_type = 'italic'
- elif self.font_bold is True and self.font_italic is True:
- font_to_geo_type = 'bi'
- elif self.font_bold is False and self.font_italic is False:
- font_to_geo_type = 'regular'
-
- string_to_geo = self.text_input_entry.get_value()
- font_to_geo_size = self.font_size_cb.get_value()
-
- self.text_path = self.f_parse.font_to_geometry(char_string=string_to_geo, font_name=self.font_name,
- font_size=font_to_geo_size,
- font_type=font_to_geo_type,
- units=self.app.app_units.upper())
-
- def font_family(self, font):
- self.text_input_entry.selectAll()
- font.setPointSize(float(self.font_size_cb.get_value()))
- self.text_input_entry.setCurrentFont(font)
- self.font_name = self.font_type_cb.currentFont().family()
-
- def font_size(self):
- self.text_input_entry.selectAll()
- self.text_input_entry.setFontPointSize(float(self.font_size_cb.get_value()))
-
- def on_bold_button(self):
- if self.font_bold_tb.isChecked():
- self.text_input_entry.selectAll()
- self.text_input_entry.setFontWeight(QtGui.QFont.Weight.Bold)
- self.font_bold = True
- else:
- self.text_input_entry.selectAll()
- self.text_input_entry.setFontWeight(QtGui.QFont.Weight.Normal)
- self.font_bold = False
-
- def on_italic_button(self):
- if self.font_italic_tb.isChecked():
- self.text_input_entry.selectAll()
- self.text_input_entry.setFontItalic(True)
- self.font_italic = True
- else:
- self.text_input_entry.selectAll()
- self.text_input_entry.setFontItalic(False)
- self.font_italic = False
-
- def hide_tool(self):
- self.text_tool_frame.hide()
- self.app.ui.notebook.setCurrentWidget(self.app.ui.properties_tab)
- # self.app.ui.splitter.setSizes([0, 1])
- self.app.ui.notebook.setTabText(2, _("Tool"))
diff --git a/appEditors/plugins/GeoTransformationPlugin.py b/appEditors/plugins/GeoTransformationPlugin.py
index 296bfd72..bebadce3 100644
--- a/appEditors/plugins/GeoTransformationPlugin.py
+++ b/appEditors/plugins/GeoTransformationPlugin.py
@@ -11,14 +11,6 @@ class TransformEditorTool(AppTool):
Inputs to specify how to paint the selected polygons.
"""
- pluginName = _("Transform Tool")
- rotateName = _("Rotate")
- skewName = _("Skew/Shear")
- scaleName = _("Scale")
- flipName = _("Mirror (Flip)")
- offsetName = _("Offset")
- bufferName = _("Buffer")
-
def __init__(self, app, draw_app):
AppTool.__init__(self, app)
@@ -26,390 +18,32 @@ class TransformEditorTool(AppTool):
self.draw_app = draw_app
self.decimals = self.app.decimals
- # ## Title
- title_label = FCLabel("%s" % self.pluginName)
- title_label.setStyleSheet("""
- QLabel
- {
- font-size: 16px;
- font-weight: bold;
- }
- """)
- self.layout.addWidget(title_label)
- self.layout.addWidget(FCLabel(''))
-
- # ## Layout
- grid0 = GLay(v_spacing=5, h_spacing=3, c_stretch=[0, 1, 0])
- self.layout.addLayout(grid0)
-
- grid0.addWidget(FCLabel(''))
-
- # Reference
- ref_label = FCLabel('%s:' % _("Reference"))
- ref_label.setToolTip(
- _("The reference point for Rotate, Skew, Scale, Mirror.\n"
- "Can be:\n"
- "- Origin -> it is the 0, 0 point\n"
- "- Selection -> the center of the bounding box of the selected objects\n"
- "- Point -> a custom point defined by X,Y coordinates\n"
- "- Min Selection -> the point (minx, miny) of the bounding box of the selection")
- )
- self.ref_combo = FCComboBox()
- self.ref_items = [_("Origin"), _("Selection"), _("Point"), _("Minimum")]
- self.ref_combo.addItems(self.ref_items)
-
- grid0.addWidget(ref_label, 0, 0)
- grid0.addWidget(self.ref_combo, 0, 1, 1, 2)
-
- self.point_label = FCLabel('%s:' % _("Value"))
- self.point_label.setToolTip(
- _("A point of reference in format X,Y.")
- )
- self.point_entry = NumericalEvalTupleEntry()
-
- grid0.addWidget(self.point_label, 1, 0)
- grid0.addWidget(self.point_entry, 1, 1, 1, 2)
-
- self.point_button = FCButton(_("Add"))
- self.point_button.setToolTip(
- _("Add point coordinates from clipboard.")
- )
- grid0.addWidget(self.point_button, 2, 0, 1, 3)
-
- separator_line = QtWidgets.QFrame()
- separator_line.setFrameShape(QtWidgets.QFrame.Shape.HLine)
- separator_line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
- grid0.addWidget(separator_line, 5, 0, 1, 3)
-
- # ## Rotate Title
- rotate_title_label = FCLabel("%s" % self.rotateName)
- grid0.addWidget(rotate_title_label, 6, 0, 1, 3)
-
- self.rotate_label = FCLabel('%s:' % _("Angle"))
- self.rotate_label.setToolTip(
- _("Angle, in degrees.\n"
- "Float number between -360 and 359.\n"
- "Positive numbers for CW motion.\n"
- "Negative numbers for CCW motion.")
- )
-
- self.rotate_entry = FCDoubleSpinner(callback=self.confirmation_message)
- self.rotate_entry.set_precision(self.decimals)
- self.rotate_entry.setSingleStep(45)
- self.rotate_entry.setWrapping(True)
- self.rotate_entry.set_range(-360, 360)
-
- # self.rotate_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
-
- self.rotate_button = FCButton(_("Rotate"))
- self.rotate_button.setToolTip(
- _("Rotate the selected object(s).\n"
- "The point of reference is the middle of\n"
- "the bounding box for all selected objects.")
- )
- self.rotate_button.setMinimumWidth(90)
-
- grid0.addWidget(self.rotate_label, 7, 0)
- grid0.addWidget(self.rotate_entry, 7, 1)
- grid0.addWidget(self.rotate_button, 7, 2)
-
- separator_line = QtWidgets.QFrame()
- separator_line.setFrameShape(QtWidgets.QFrame.Shape.HLine)
- separator_line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
- grid0.addWidget(separator_line, 8, 0, 1, 3)
-
- # ## Skew Title
- skew_title_label = FCLabel("%s" % self.skewName)
- grid0.addWidget(skew_title_label, 9, 0, 1, 2)
-
- self.skew_link_cb = FCCheckBox()
- self.skew_link_cb.setText(_("Link"))
- self.skew_link_cb.setToolTip(
- _("Link the Y entry to X entry and copy its content.")
- )
-
- grid0.addWidget(self.skew_link_cb, 9, 2)
-
- self.skewx_label = FCLabel('%s:' % _("X angle"))
- self.skewx_label.setToolTip(
- _("Angle for Skew action, in degrees.\n"
- "Float number between -360 and 360.")
- )
- self.skewx_entry = FCDoubleSpinner(callback=self.confirmation_message)
- # self.skewx_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
- self.skewx_entry.set_precision(self.decimals)
- self.skewx_entry.set_range(-360, 360)
-
- self.skewx_button = FCButton(_("Skew X"))
- self.skewx_button.setToolTip(
- _("Skew/shear the selected object(s).\n"
- "The point of reference is the middle of\n"
- "the bounding box for all selected objects."))
- self.skewx_button.setMinimumWidth(90)
-
- grid0.addWidget(self.skewx_label, 10, 0)
- grid0.addWidget(self.skewx_entry, 10, 1)
- grid0.addWidget(self.skewx_button, 10, 2)
-
- self.skewy_label = FCLabel('%s:' % _("Y angle"))
- self.skewy_label.setToolTip(
- _("Angle for Skew action, in degrees.\n"
- "Float number between -360 and 360.")
- )
- self.skewy_entry = FCDoubleSpinner(callback=self.confirmation_message)
- # self.skewy_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
- self.skewy_entry.set_precision(self.decimals)
- self.skewy_entry.set_range(-360, 360)
-
- self.skewy_button = FCButton(_("Skew Y"))
- self.skewy_button.setToolTip(
- _("Skew/shear the selected object(s).\n"
- "The point of reference is the middle of\n"
- "the bounding box for all selected objects."))
- self.skewy_button.setMinimumWidth(90)
-
- grid0.addWidget(self.skewy_label, 12, 0)
- grid0.addWidget(self.skewy_entry, 12, 1)
- grid0.addWidget(self.skewy_button, 12, 2)
-
- self.ois_sk = OptionalInputSection(self.skew_link_cb, [self.skewy_label, self.skewy_entry, self.skewy_button],
- logic=False)
-
- separator_line = QtWidgets.QFrame()
- separator_line.setFrameShape(QtWidgets.QFrame.Shape.HLine)
- separator_line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
- grid0.addWidget(separator_line, 14, 0, 1, 3)
-
- # ## Scale Title
- scale_title_label = FCLabel("%s" % self.scaleName)
- grid0.addWidget(scale_title_label, 15, 0, 1, 2)
-
- self.scale_link_cb = FCCheckBox()
- self.scale_link_cb.setText(_("Link"))
- self.scale_link_cb.setToolTip(
- _("Link the Y entry to X entry and copy its content.")
- )
-
- grid0.addWidget(self.scale_link_cb, 15, 2)
-
- self.scalex_label = FCLabel('%s:' % _("X factor"))
- self.scalex_label.setToolTip(
- _("Factor for scaling on X axis.")
- )
- self.scalex_entry = FCDoubleSpinner(callback=self.confirmation_message)
- # self.scalex_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
- self.scalex_entry.set_precision(self.decimals)
- self.scalex_entry.setMinimum(-1e6)
-
- self.scalex_button = FCButton(_("Scale X"))
- self.scalex_button.setToolTip(
- _("Scale the selected object(s).\n"
- "The point of reference depends on \n"
- "the Scale reference checkbox state."))
- self.scalex_button.setMinimumWidth(90)
-
- grid0.addWidget(self.scalex_label, 17, 0)
- grid0.addWidget(self.scalex_entry, 17, 1)
- grid0.addWidget(self.scalex_button, 17, 2)
-
- self.scaley_label = FCLabel('%s:' % _("Y factor"))
- self.scaley_label.setToolTip(
- _("Factor for scaling on Y axis.")
- )
- self.scaley_entry = FCDoubleSpinner(callback=self.confirmation_message)
- # self.scaley_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
- self.scaley_entry.set_precision(self.decimals)
- self.scaley_entry.setMinimum(-1e6)
-
- self.scaley_button = FCButton(_("Scale Y"))
- self.scaley_button.setToolTip(
- _("Scale the selected object(s).\n"
- "The point of reference depends on \n"
- "the Scale reference checkbox state."))
- self.scaley_button.setMinimumWidth(90)
-
- grid0.addWidget(self.scaley_label, 19, 0)
- grid0.addWidget(self.scaley_entry, 19, 1)
- grid0.addWidget(self.scaley_button, 19, 2)
-
- self.ois_s = OptionalInputSection(self.scale_link_cb,
- [
- self.scaley_label,
- self.scaley_entry,
- self.scaley_button
- ], logic=False)
-
- separator_line = QtWidgets.QFrame()
- separator_line.setFrameShape(QtWidgets.QFrame.Shape.HLine)
- separator_line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
- grid0.addWidget(separator_line, 21, 0, 1, 3)
-
- # ## Flip Title
- flip_title_label = FCLabel("%s" % self.flipName)
- grid0.addWidget(flip_title_label, 23, 0, 1, 3)
-
- self.flipx_button = FCButton(_("Flip on X"))
- self.flipx_button.setToolTip(
- _("Flip the selected object(s) over the X axis.")
- )
-
- self.flipy_button = FCButton(_("Flip on Y"))
- self.flipy_button.setToolTip(
- _("Flip the selected object(s) over the X axis.")
- )
-
- hlay0 = QtWidgets.QHBoxLayout()
- grid0.addLayout(hlay0, 25, 0, 1, 3)
-
- hlay0.addWidget(self.flipx_button)
- hlay0.addWidget(self.flipy_button)
-
- separator_line = QtWidgets.QFrame()
- separator_line.setFrameShape(QtWidgets.QFrame.Shape.HLine)
- separator_line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
- grid0.addWidget(separator_line, 27, 0, 1, 3)
-
- # ## Offset Title
- offset_title_label = FCLabel("%s" % self.offsetName)
- grid0.addWidget(offset_title_label, 29, 0, 1, 3)
-
- self.offx_label = FCLabel('%s:' % _("X val"))
- self.offx_label.setToolTip(
- _("Distance to offset on X axis. In current units.")
- )
- self.offx_entry = FCDoubleSpinner(callback=self.confirmation_message)
- # self.offx_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
- self.offx_entry.set_precision(self.decimals)
- self.offx_entry.setMinimum(-1e6)
-
- self.offx_button = FCButton(_("Offset X"))
- self.offx_button.setToolTip(
- _("Offset the selected object(s).\n"
- "The point of reference is the middle of\n"
- "the bounding box for all selected objects.\n"))
- self.offx_button.setMinimumWidth(90)
-
- grid0.addWidget(self.offx_label, 31, 0)
- grid0.addWidget(self.offx_entry, 31, 1)
- grid0.addWidget(self.offx_button, 31, 2)
-
- self.offy_label = FCLabel('%s:' % _("Y val"))
- self.offy_label.setToolTip(
- _("Distance to offset on Y axis. In current units.")
- )
- self.offy_entry = FCDoubleSpinner(callback=self.confirmation_message)
- # self.offy_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
- self.offy_entry.set_precision(self.decimals)
- self.offy_entry.setMinimum(-1e6)
-
- self.offy_button = FCButton(_("Offset Y"))
- self.offy_button.setToolTip(
- _("Offset the selected object(s).\n"
- "The point of reference is the middle of\n"
- "the bounding box for all selected objects.\n"))
- self.offy_button.setMinimumWidth(90)
-
- grid0.addWidget(self.offy_label, 32, 0)
- grid0.addWidget(self.offy_entry, 32, 1)
- grid0.addWidget(self.offy_button, 32, 2)
-
- separator_line = QtWidgets.QFrame()
- separator_line.setFrameShape(QtWidgets.QFrame.Shape.HLine)
- separator_line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
- grid0.addWidget(separator_line, 34, 0, 1, 3)
-
- # ## Buffer Title
- buffer_title_label = FCLabel("%s" % self.bufferName)
- grid0.addWidget(buffer_title_label, 35, 0, 1, 2)
-
- self.buffer_rounded_cb = FCCheckBox('%s' % _("Rounded"))
- self.buffer_rounded_cb.setToolTip(
- _("If checked then the buffer will surround the buffered shape,\n"
- "every corner will be rounded.\n"
- "If not checked then the buffer will follow the exact geometry\n"
- "of the buffered shape.")
- )
-
- grid0.addWidget(self.buffer_rounded_cb, 35, 2)
-
- self.buffer_label = FCLabel('%s:' % _("Distance"))
- self.buffer_label.setToolTip(
- _("A positive value will create the effect of dilation,\n"
- "while a negative value will create the effect of erosion.\n"
- "Each geometry element of the object will be increased\n"
- "or decreased with the 'distance'.")
- )
-
- self.buffer_entry = FCDoubleSpinner(callback=self.confirmation_message)
- self.buffer_entry.set_precision(self.decimals)
- self.buffer_entry.setSingleStep(0.1)
- self.buffer_entry.setWrapping(True)
- self.buffer_entry.set_range(-10000.0000, 10000.0000)
-
- self.buffer_button = FCButton(_("Buffer D"))
- self.buffer_button.setToolTip(
- _("Create the buffer effect on each geometry,\n"
- "element from the selected object, using the distance.")
- )
- self.buffer_button.setMinimumWidth(90)
-
- grid0.addWidget(self.buffer_label, 37, 0)
- grid0.addWidget(self.buffer_entry, 37, 1)
- grid0.addWidget(self.buffer_button, 37, 2)
-
- self.buffer_factor_label = FCLabel('%s:' % _("Value"))
- self.buffer_factor_label.setToolTip(
- _("A positive value will create the effect of dilation,\n"
- "while a negative value will create the effect of erosion.\n"
- "Each geometry element of the object will be increased\n"
- "or decreased to fit the 'Value'. Value is a percentage\n"
- "of the initial dimension.")
- )
-
- self.buffer_factor_entry = FCDoubleSpinner(callback=self.confirmation_message, suffix='%')
- self.buffer_factor_entry.set_range(-100.0000, 1000.0000)
- self.buffer_factor_entry.set_precision(self.decimals)
- self.buffer_factor_entry.setWrapping(True)
- self.buffer_factor_entry.setSingleStep(1)
-
- self.buffer_factor_button = FCButton(_("Buffer F"))
- self.buffer_factor_button.setToolTip(
- _("Create the buffer effect on each geometry,\n"
- "element from the selected object, using the factor.")
- )
- self.buffer_factor_button.setMinimumWidth(90)
-
- grid0.addWidget(self.buffer_factor_label, 38, 0)
- grid0.addWidget(self.buffer_factor_entry, 38, 1)
- grid0.addWidget(self.buffer_factor_button, 38, 2)
-
- grid0.addWidget(FCLabel(''), 42, 0, 1, 3)
-
- self.layout.addStretch()
-
- # Signals
- self.ref_combo.currentIndexChanged.connect(self.on_reference_changed)
- self.point_button.clicked.connect(lambda: self.on_add_coords)
-
- self.rotate_button.clicked.connect(lambda: self.on_rotate)
-
- self.skewx_button.clicked.connect(lambda: self.on_skewx)
- self.skewy_button.clicked.connect(lambda: self.on_skewy)
-
- self.scalex_button.clicked.connect(lambda: self.on_scalex)
- self.scaley_button.clicked.connect(lambda: self.on_scaley)
-
- self.offx_button.clicked.connect(lambda: self.on_offx)
- self.offy_button.clicked.connect(lambda: self.on_offy)
-
- self.flipx_button.clicked.connect(lambda: self.on_flipx)
- self.flipy_button.clicked.connect(lambda: self.on_flipy)
-
- self.buffer_button.clicked.connect(lambda: self.on_buffer_by_distance)
- self.buffer_factor_button.clicked.connect(lambda: self.on_buffer_by_factor)
+ self.ui = TransformationEditorUI(layout=self.layout, transform_class=self)
+ self.connect_signals_at_init()
self.set_tool_ui()
+ def connect_signals_at_init(self):
+ # Signals
+ self.ui.point_button.clicked.connect(lambda: self.on_add_coords())
+
+ self.ui.rotate_button.clicked.connect(lambda: self.on_rotate())
+
+ self.ui.skewx_button.clicked.connect(lambda: self.on_skewx())
+ self.ui.skewy_button.clicked.connect(lambda: self.on_skewy())
+
+ self.ui.scalex_button.clicked.connect(lambda: self.on_scalex())
+ self.ui.scaley_button.clicked.connect(lambda: self.on_scaley())
+
+ self.ui.offx_button.clicked.connect(lambda: self.on_offx())
+ self.ui.offy_button.clicked.connect(lambda: self.on_offy())
+
+ self.ui.flipx_button.clicked.connect(lambda: self.on_flipx())
+ self.ui.flipy_button.clicked.connect(lambda: self.on_flipy())
+
+ self.ui.buffer_button.clicked.connect(lambda: self.on_buffer_by_distance())
+ self.ui.buffer_factor_button.clicked.connect(lambda: self.on_buffer_by_factor())
+
def run(self, toggle=True):
self.app.defaults.report_usage("Geo Editor Transform Tool()")
@@ -453,7 +87,7 @@ class TransformEditorTool(AppTool):
AppTool.run(self)
self.set_tool_ui()
- self.app.ui.notebook.setTabText(2, _("Transform Tool"))
+ self.app.ui.notebook.setTabText(2, _("Transformation"))
def on_tab_close(self):
self.draw_app.select_tool("select")
@@ -467,29 +101,29 @@ class TransformEditorTool(AppTool):
ref_val = self.app.options["tools_transform_reference"]
if ref_val == _("Object"):
ref_val = _("Selection")
- self.ref_combo.set_value(ref_val)
- self.point_entry.set_value(self.app.options["tools_transform_ref_point"])
- self.rotate_entry.set_value(self.app.options["tools_transform_rotate"])
+ self.ui.ref_combo.set_value(ref_val)
+ self.ui.point_entry.set_value(self.app.options["tools_transform_ref_point"])
+ self.ui.rotate_entry.set_value(self.app.options["tools_transform_rotate"])
- self.skewx_entry.set_value(self.app.options["tools_transform_skew_x"])
- self.skewy_entry.set_value(self.app.options["tools_transform_skew_y"])
- self.skew_link_cb.set_value(self.app.options["tools_transform_skew_link"])
+ self.ui.skewx_entry.set_value(self.app.options["tools_transform_skew_x"])
+ self.ui.skewy_entry.set_value(self.app.options["tools_transform_skew_y"])
+ self.ui.skew_link_cb.set_value(self.app.options["tools_transform_skew_link"])
- self.scalex_entry.set_value(self.app.options["tools_transform_scale_x"])
- self.scaley_entry.set_value(self.app.options["tools_transform_scale_y"])
- self.scale_link_cb.set_value(self.app.options["tools_transform_scale_link"])
+ self.ui.scalex_entry.set_value(self.app.options["tools_transform_scale_x"])
+ self.ui.scaley_entry.set_value(self.app.options["tools_transform_scale_y"])
+ self.ui.scale_link_cb.set_value(self.app.options["tools_transform_scale_link"])
- self.offx_entry.set_value(self.app.options["tools_transform_offset_x"])
- self.offy_entry.set_value(self.app.options["tools_transform_offset_y"])
+ self.ui.offx_entry.set_value(self.app.options["tools_transform_offset_x"])
+ self.ui.offy_entry.set_value(self.app.options["tools_transform_offset_y"])
- self.buffer_entry.set_value(self.app.options["tools_transform_buffer_dis"])
- self.buffer_factor_entry.set_value(self.app.options["tools_transform_buffer_factor"])
- self.buffer_rounded_cb.set_value(self.app.options["tools_transform_buffer_corner"])
+ self.ui.buffer_entry.set_value(self.app.options["tools_transform_buffer_dis"])
+ self.ui.buffer_factor_entry.set_value(self.app.options["tools_transform_buffer_factor"])
+ self.ui.buffer_rounded_cb.set_value(self.app.options["tools_transform_buffer_corner"])
# initial state is hidden
- self.point_label.hide()
- self.point_entry.hide()
- self.point_button.hide()
+ self.ui.point_label.hide()
+ self.ui.point_entry.hide()
+ self.ui.point_button.hide()
def template(self):
if not self.draw_app.selected:
@@ -502,22 +136,11 @@ class TransformEditorTool(AppTool):
self.app.ui.splitter.setSizes([0, 1])
- def on_reference_changed(self, index):
- if index == 0 or index == 1: # "Origin" or "Selection" reference
- self.point_label.hide()
- self.point_entry.hide()
- self.point_button.hide()
-
- elif index == 2: # "Point" reference
- self.point_label.show()
- self.point_entry.show()
- self.point_button.show()
-
def on_calculate_reference(self, ref_index=None):
if ref_index:
ref_val = ref_index
else:
- ref_val = self.ref_combo.currentIndex()
+ ref_val = self.ui.ref_combo.currentIndex()
if ref_val == 0: # "Origin" reference
return 0, 0
@@ -532,7 +155,7 @@ class TransformEditorTool(AppTool):
self.app.inform.emit('[ERROR_NOTCL] %s' % _("No shape selected."))
return "fail"
elif ref_val == 2: # "Point" reference
- point_val = self.point_entry.get_value()
+ point_val = self.ui.point_entry.get_value()
try:
px, py = eval('{}'.format(point_val))
return px, py
@@ -557,10 +180,10 @@ class TransformEditorTool(AppTool):
def on_add_coords(self):
val = self.app.clipboard.text()
- self.point_entry.set_value(val)
+ self.ui.point_entry.set_value(val)
def on_rotate(self, val=None, ref=None):
- value = float(self.rotate_entry.get_value()) if val is None else val
+ value = float(self.ui.rotate_entry.get_value()) if val is None else val
if value == 0:
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Rotate transformation can not be done for a value of 0."))
return
@@ -584,15 +207,12 @@ class TransformEditorTool(AppTool):
self.app.worker_task.emit({'fcn': self.on_flip, 'params': [axis, point]})
def on_skewx(self, val=None, ref=None):
- xvalue = float(self.skewx_entry.get_value()) if val is None else val
+ xvalue = float(self.ui.skewx_entry.get_value()) if val is None else val
if xvalue == 0:
return
- if self.skew_link_cb.get_value():
- yvalue = xvalue
- else:
- yvalue = 0
+ yvalue = xvalue if self.ui.skew_link_cb.get_value() else 0
axis = 'X'
point = self.on_calculate_reference() if ref is None else self.on_calculate_reference(ref_index=ref)
@@ -603,7 +223,7 @@ class TransformEditorTool(AppTool):
def on_skewy(self, val=None, ref=None):
xvalue = 0
- yvalue = float(self.skewy_entry.get_value()) if val is None else val
+ yvalue = float(self.ui.skewy_entry.get_value()) if val is None else val
if yvalue == 0:
return
@@ -616,17 +236,14 @@ class TransformEditorTool(AppTool):
self.app.worker_task.emit({'fcn': self.on_skew, 'params': [axis, xvalue, yvalue, point]})
def on_scalex(self, val=None, ref=None):
- xvalue = float(self.scalex_entry.get_value()) if val is None else val
+ xvalue = float(self.ui.scalex_entry.get_value()) if val is None else val
if xvalue == 0 or xvalue == 1:
self.app.inform.emit('[WARNING_NOTCL] %s' %
_("Scale transformation can not be done for a factor of 0 or 1."))
return
- if self.scale_link_cb.get_value():
- yvalue = xvalue
- else:
- yvalue = 1
+ yvalue = xvalue if self.ui.scale_link_cb.get_value() else 1
axis = 'X'
point = self.on_calculate_reference() if ref is None else self.on_calculate_reference(ref_index=ref)
@@ -637,7 +254,7 @@ class TransformEditorTool(AppTool):
def on_scaley(self, val=None, ref=None):
xvalue = 1
- yvalue = float(self.scaley_entry.get_value()) if val is None else val
+ yvalue = float(self.ui.scaley_entry.get_value()) if val is None else val
if yvalue == 0 or yvalue == 1:
self.app.inform.emit('[WARNING_NOTCL] %s' %
@@ -652,7 +269,7 @@ class TransformEditorTool(AppTool):
self.app.worker_task.emit({'fcn': self.on_scale, 'params': [axis, xvalue, yvalue, point]})
def on_offx(self, val=None):
- value = float(self.offx_entry.get_value()) if val is None else val
+ value = float(self.ui.offx_entry.get_value()) if val is None else val
if value == 0:
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Offset transformation can not be done for a value of 0."))
return
@@ -661,7 +278,7 @@ class TransformEditorTool(AppTool):
self.app.worker_task.emit({'fcn': self.on_offset, 'params': [axis, value]})
def on_offy(self, val=None):
- value = float(self.offy_entry.get_value()) if val is None else val
+ value = float(self.ui.offy_entry.get_value()) if val is None else val
if value == 0:
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Offset transformation can not be done for a value of 0."))
return
@@ -670,14 +287,14 @@ class TransformEditorTool(AppTool):
self.app.worker_task.emit({'fcn': self.on_offset, 'params': [axis, value]})
def on_buffer_by_distance(self):
- value = self.buffer_entry.get_value()
- join = 1 if self.buffer_rounded_cb.get_value() else 2
+ value = self.ui.buffer_entry.get_value()
+ join = 1 if self.ui.buffer_rounded_cb.get_value() else 2
self.app.worker_task.emit({'fcn': self.on_buffer_action, 'params': [value, join]})
def on_buffer_by_factor(self):
- value = 1 + (self.buffer_factor_entry.get_value() / 100.0)
- join = 1 if self.buffer_rounded_cb.get_value() else 2
+ value = 1 + (self.ui.buffer_factor_entry.get_value() / 100.0)
+ join = 1 if self.ui.buffer_rounded_cb.get_value() else 2
# tell the buffer method to use the factor
factor = True
@@ -975,3 +592,391 @@ class TransformEditorTool(AppTool):
return lst.bounds()
return bounds_rec(shapelist)
+
+
+class TransformationEditorUI:
+ pluginName = _("Transformation")
+ rotateName = _("Rotate")
+ skewName = _("Skew/Shear")
+ scaleName = _("Scale")
+ flipName = _("Mirror")
+ offsetName = _("Offset")
+ bufferName = _("Buffer")
+
+ def __init__(self, layout, transform_class):
+ self.transform_class = transform_class
+ self.decimals = self.transform_class.app.decimals
+ self.layout = layout
+
+ # ## Title
+ title_label = FCLabel("%s" % self.pluginName)
+ title_label.setStyleSheet("""
+ QLabel
+ {
+ font-size: 16px;
+ font-weight: bold;
+ }
+ """)
+ self.layout.addWidget(title_label)
+ self.layout.addWidget(FCLabel(''))
+
+ # ## Layout
+ grid0 = GLay(v_spacing=5, h_spacing=3, c_stretch=[0, 1, 0])
+ self.layout.addLayout(grid0)
+
+ grid0.addWidget(FCLabel(''))
+
+ # Reference
+ ref_label = FCLabel('%s:' % _("Reference"))
+ ref_label.setToolTip(
+ _("The reference point for Rotate, Skew, Scale, Mirror.\n"
+ "Can be:\n"
+ "- Origin -> it is the 0, 0 point\n"
+ "- Selection -> the center of the bounding box of the selected objects\n"
+ "- Point -> a custom point defined by X,Y coordinates\n"
+ "- Min Selection -> the point (minx, miny) of the bounding box of the selection")
+ )
+ self.ref_combo = FCComboBox()
+ self.ref_items = [_("Origin"), _("Selection"), _("Point"), _("Minimum")]
+ self.ref_combo.addItems(self.ref_items)
+
+ grid0.addWidget(ref_label, 0, 0)
+ grid0.addWidget(self.ref_combo, 0, 1, 1, 2)
+
+ self.point_label = FCLabel('%s:' % _("Value"))
+ self.point_label.setToolTip(
+ _("A point of reference in format X,Y.")
+ )
+ self.point_entry = NumericalEvalTupleEntry()
+
+ grid0.addWidget(self.point_label, 1, 0)
+ grid0.addWidget(self.point_entry, 1, 1, 1, 2)
+
+ self.point_button = FCButton(_("Add"))
+ self.point_button.setToolTip(
+ _("Add point coordinates from clipboard.")
+ )
+ grid0.addWidget(self.point_button, 2, 0, 1, 3)
+
+ separator_line = QtWidgets.QFrame()
+ separator_line.setFrameShape(QtWidgets.QFrame.Shape.HLine)
+ separator_line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
+ grid0.addWidget(separator_line, 5, 0, 1, 3)
+
+ # ## Rotate Title
+ rotate_title_label = FCLabel("%s" % self.rotateName)
+ grid0.addWidget(rotate_title_label, 6, 0, 1, 3)
+
+ self.rotate_label = FCLabel('%s:' % _("Angle"))
+ self.rotate_label.setToolTip(
+ _("Angle, in degrees.\n"
+ "Float number between -360 and 359.\n"
+ "Positive numbers for CW motion.\n"
+ "Negative numbers for CCW motion.")
+ )
+
+ self.rotate_entry = FCDoubleSpinner(callback=self.transform_class.confirmation_message)
+ self.rotate_entry.set_precision(self.decimals)
+ self.rotate_entry.setSingleStep(45)
+ self.rotate_entry.setWrapping(True)
+ self.rotate_entry.set_range(-360, 360)
+
+ # self.rotate_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+
+ self.rotate_button = FCButton(_("Rotate"))
+ self.rotate_button.setToolTip(
+ _("Rotate the selected object(s).\n"
+ "The point of reference is the middle of\n"
+ "the bounding box for all selected objects.")
+ )
+ self.rotate_button.setMinimumWidth(90)
+
+ grid0.addWidget(self.rotate_label, 7, 0)
+ grid0.addWidget(self.rotate_entry, 7, 1)
+ grid0.addWidget(self.rotate_button, 7, 2)
+
+ separator_line = QtWidgets.QFrame()
+ separator_line.setFrameShape(QtWidgets.QFrame.Shape.HLine)
+ separator_line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
+ grid0.addWidget(separator_line, 8, 0, 1, 3)
+
+ # ## Skew Title
+ skew_title_label = FCLabel("%s" % self.skewName)
+ grid0.addWidget(skew_title_label, 9, 0, 1, 2)
+
+ self.skew_link_cb = FCCheckBox()
+ self.skew_link_cb.setText(_("Link"))
+ self.skew_link_cb.setToolTip(
+ _("Link the Y entry to X entry and copy its content.")
+ )
+
+ grid0.addWidget(self.skew_link_cb, 9, 2)
+
+ self.skewx_label = FCLabel('%s:' % _("X angle"))
+ self.skewx_label.setToolTip(
+ _("Angle for Skew action, in degrees.\n"
+ "Float number between -360 and 360.")
+ )
+ self.skewx_entry = FCDoubleSpinner(callback=self.transform_class.confirmation_message)
+ # self.skewx_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+ self.skewx_entry.set_precision(self.decimals)
+ self.skewx_entry.set_range(-360, 360)
+
+ self.skewx_button = FCButton(_("Skew X"))
+ self.skewx_button.setToolTip(
+ _("Skew/shear the selected object(s).\n"
+ "The point of reference is the middle of\n"
+ "the bounding box for all selected objects."))
+ self.skewx_button.setMinimumWidth(90)
+
+ grid0.addWidget(self.skewx_label, 10, 0)
+ grid0.addWidget(self.skewx_entry, 10, 1)
+ grid0.addWidget(self.skewx_button, 10, 2)
+
+ self.skewy_label = FCLabel('%s:' % _("Y angle"))
+ self.skewy_label.setToolTip(
+ _("Angle for Skew action, in degrees.\n"
+ "Float number between -360 and 360.")
+ )
+ self.skewy_entry = FCDoubleSpinner(callback=self.transform_class.confirmation_message)
+ # self.skewy_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+ self.skewy_entry.set_precision(self.decimals)
+ self.skewy_entry.set_range(-360, 360)
+
+ self.skewy_button = FCButton(_("Skew Y"))
+ self.skewy_button.setToolTip(
+ _("Skew/shear the selected object(s).\n"
+ "The point of reference is the middle of\n"
+ "the bounding box for all selected objects."))
+ self.skewy_button.setMinimumWidth(90)
+
+ grid0.addWidget(self.skewy_label, 12, 0)
+ grid0.addWidget(self.skewy_entry, 12, 1)
+ grid0.addWidget(self.skewy_button, 12, 2)
+
+ self.ois_sk = OptionalInputSection(self.skew_link_cb, [self.skewy_label, self.skewy_entry, self.skewy_button],
+ logic=False)
+
+ separator_line = QtWidgets.QFrame()
+ separator_line.setFrameShape(QtWidgets.QFrame.Shape.HLine)
+ separator_line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
+ grid0.addWidget(separator_line, 14, 0, 1, 3)
+
+ # ## Scale Title
+ scale_title_label = FCLabel("%s" % self.scaleName)
+ grid0.addWidget(scale_title_label, 15, 0, 1, 2)
+
+ self.scale_link_cb = FCCheckBox()
+ self.scale_link_cb.setText(_("Link"))
+ self.scale_link_cb.setToolTip(
+ _("Link the Y entry to X entry and copy its content.")
+ )
+
+ grid0.addWidget(self.scale_link_cb, 15, 2)
+
+ self.scalex_label = FCLabel('%s:' % _("X factor"))
+ self.scalex_label.setToolTip(
+ _("Factor for scaling on X axis.")
+ )
+ self.scalex_entry = FCDoubleSpinner(callback=self.transform_class.confirmation_message)
+ # self.scalex_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+ self.scalex_entry.set_precision(self.decimals)
+ self.scalex_entry.setMinimum(-1e6)
+
+ self.scalex_button = FCButton(_("Scale X"))
+ self.scalex_button.setToolTip(
+ _("Scale the selected object(s).\n"
+ "The point of reference depends on \n"
+ "the Scale reference checkbox state."))
+ self.scalex_button.setMinimumWidth(90)
+
+ grid0.addWidget(self.scalex_label, 17, 0)
+ grid0.addWidget(self.scalex_entry, 17, 1)
+ grid0.addWidget(self.scalex_button, 17, 2)
+
+ self.scaley_label = FCLabel('%s:' % _("Y factor"))
+ self.scaley_label.setToolTip(
+ _("Factor for scaling on Y axis.")
+ )
+ self.scaley_entry = FCDoubleSpinner(callback=self.transform_class.confirmation_message)
+ # self.scaley_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+ self.scaley_entry.set_precision(self.decimals)
+ self.scaley_entry.setMinimum(-1e6)
+
+ self.scaley_button = FCButton(_("Scale Y"))
+ self.scaley_button.setToolTip(
+ _("Scale the selected object(s).\n"
+ "The point of reference depends on \n"
+ "the Scale reference checkbox state."))
+ self.scaley_button.setMinimumWidth(90)
+
+ grid0.addWidget(self.scaley_label, 19, 0)
+ grid0.addWidget(self.scaley_entry, 19, 1)
+ grid0.addWidget(self.scaley_button, 19, 2)
+
+ self.ois_s = OptionalInputSection(self.scale_link_cb,
+ [
+ self.scaley_label,
+ self.scaley_entry,
+ self.scaley_button
+ ], logic=False)
+
+ separator_line = QtWidgets.QFrame()
+ separator_line.setFrameShape(QtWidgets.QFrame.Shape.HLine)
+ separator_line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
+ grid0.addWidget(separator_line, 21, 0, 1, 3)
+
+ # ## Flip Title
+ flip_title_label = FCLabel("%s" % self.flipName)
+ grid0.addWidget(flip_title_label, 23, 0, 1, 3)
+
+ self.flipx_button = FCButton(_("Flip on X"))
+ self.flipx_button.setToolTip(
+ _("Flip the selected object(s) over the X axis.")
+ )
+
+ self.flipy_button = FCButton(_("Flip on Y"))
+ self.flipy_button.setToolTip(
+ _("Flip the selected object(s) over the X axis.")
+ )
+
+ hlay0 = QtWidgets.QHBoxLayout()
+ grid0.addLayout(hlay0, 25, 0, 1, 3)
+
+ hlay0.addWidget(self.flipx_button)
+ hlay0.addWidget(self.flipy_button)
+
+ separator_line = QtWidgets.QFrame()
+ separator_line.setFrameShape(QtWidgets.QFrame.Shape.HLine)
+ separator_line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
+ grid0.addWidget(separator_line, 27, 0, 1, 3)
+
+ # ## Offset Title
+ offset_title_label = FCLabel("%s" % self.offsetName)
+ grid0.addWidget(offset_title_label, 29, 0, 1, 3)
+
+ self.offx_label = FCLabel('%s:' % _("X val"))
+ self.offx_label.setToolTip(
+ _("Distance to offset on X axis. In current units.")
+ )
+ self.offx_entry = FCDoubleSpinner(callback=self.transform_class.confirmation_message)
+ # self.offx_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+ self.offx_entry.set_precision(self.decimals)
+ self.offx_entry.setMinimum(-1e6)
+
+ self.offx_button = FCButton(_("Offset X"))
+ self.offx_button.setToolTip(
+ _("Offset the selected object(s).\n"
+ "The point of reference is the middle of\n"
+ "the bounding box for all selected objects.\n"))
+ self.offx_button.setMinimumWidth(90)
+
+ grid0.addWidget(self.offx_label, 31, 0)
+ grid0.addWidget(self.offx_entry, 31, 1)
+ grid0.addWidget(self.offx_button, 31, 2)
+
+ self.offy_label = FCLabel('%s:' % _("Y val"))
+ self.offy_label.setToolTip(
+ _("Distance to offset on Y axis. In current units.")
+ )
+ self.offy_entry = FCDoubleSpinner(callback=self.transform_class.confirmation_message)
+ # self.offy_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+ self.offy_entry.set_precision(self.decimals)
+ self.offy_entry.setMinimum(-1e6)
+
+ self.offy_button = FCButton(_("Offset Y"))
+ self.offy_button.setToolTip(
+ _("Offset the selected object(s).\n"
+ "The point of reference is the middle of\n"
+ "the bounding box for all selected objects.\n"))
+ self.offy_button.setMinimumWidth(90)
+
+ grid0.addWidget(self.offy_label, 32, 0)
+ grid0.addWidget(self.offy_entry, 32, 1)
+ grid0.addWidget(self.offy_button, 32, 2)
+
+ separator_line = QtWidgets.QFrame()
+ separator_line.setFrameShape(QtWidgets.QFrame.Shape.HLine)
+ separator_line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
+ grid0.addWidget(separator_line, 34, 0, 1, 3)
+
+ # ## Buffer Title
+ buffer_title_label = FCLabel("%s" % self.bufferName)
+ grid0.addWidget(buffer_title_label, 35, 0, 1, 2)
+
+ self.buffer_rounded_cb = FCCheckBox('%s' % _("Rounded"))
+ self.buffer_rounded_cb.setToolTip(
+ _("If checked then the buffer will surround the buffered shape,\n"
+ "every corner will be rounded.\n"
+ "If not checked then the buffer will follow the exact geometry\n"
+ "of the buffered shape.")
+ )
+
+ grid0.addWidget(self.buffer_rounded_cb, 35, 2)
+
+ self.buffer_label = FCLabel('%s:' % _("Distance"))
+ self.buffer_label.setToolTip(
+ _("A positive value will create the effect of dilation,\n"
+ "while a negative value will create the effect of erosion.\n"
+ "Each geometry element of the object will be increased\n"
+ "or decreased with the 'distance'.")
+ )
+
+ self.buffer_entry = FCDoubleSpinner(callback=self.transform_class.confirmation_message)
+ self.buffer_entry.set_precision(self.decimals)
+ self.buffer_entry.setSingleStep(0.1)
+ self.buffer_entry.setWrapping(True)
+ self.buffer_entry.set_range(-10000.0000, 10000.0000)
+
+ self.buffer_button = FCButton(_("Buffer D"))
+ self.buffer_button.setToolTip(
+ _("Create the buffer effect on each geometry,\n"
+ "element from the selected object, using the distance.")
+ )
+ self.buffer_button.setMinimumWidth(90)
+
+ grid0.addWidget(self.buffer_label, 37, 0)
+ grid0.addWidget(self.buffer_entry, 37, 1)
+ grid0.addWidget(self.buffer_button, 37, 2)
+
+ self.buffer_factor_label = FCLabel('%s:' % _("Value"))
+ self.buffer_factor_label.setToolTip(
+ _("A positive value will create the effect of dilation,\n"
+ "while a negative value will create the effect of erosion.\n"
+ "Each geometry element of the object will be increased\n"
+ "or decreased to fit the 'Value'. Value is a percentage\n"
+ "of the initial dimension.")
+ )
+
+ self.buffer_factor_entry = FCDoubleSpinner(callback=self.transform_class.confirmation_message, suffix='%')
+ self.buffer_factor_entry.set_range(-100.0000, 1000.0000)
+ self.buffer_factor_entry.set_precision(self.decimals)
+ self.buffer_factor_entry.setWrapping(True)
+ self.buffer_factor_entry.setSingleStep(1)
+
+ self.buffer_factor_button = FCButton(_("Buffer F"))
+ self.buffer_factor_button.setToolTip(
+ _("Create the buffer effect on each geometry,\n"
+ "element from the selected object, using the factor.")
+ )
+ self.buffer_factor_button.setMinimumWidth(90)
+
+ grid0.addWidget(self.buffer_factor_label, 38, 0)
+ grid0.addWidget(self.buffer_factor_entry, 38, 1)
+ grid0.addWidget(self.buffer_factor_button, 38, 2)
+
+ grid0.addWidget(FCLabel(''), 42, 0, 1, 3)
+
+ self.layout.addStretch()
+ self.ref_combo.currentIndexChanged.connect(self.on_reference_changed)
+
+ def on_reference_changed(self, index):
+ if index == 0 or index == 1: # "Origin" or "Selection" reference
+ self.point_label.hide()
+ self.point_entry.hide()
+ self.point_button.hide()
+
+ elif index == 2: # "Point" reference
+ self.point_label.show()
+ self.point_entry.show()
+ self.point_button.show()
diff --git a/appGUI/MainGUI.py b/appGUI/MainGUI.py
index 653ce1b9..a83e93a4 100644
--- a/appGUI/MainGUI.py
+++ b/appGUI/MainGUI.py
@@ -726,7 +726,7 @@ class MainGUI(QtWidgets.QMainWindow):
)
self.geo_transform_menuitem = self.geo_editor_menu.addAction(
QtGui.QIcon(self.app.resource_location + '/transform.png'),
- '%s\t%s' % (_("Transform Tool"), _('Alt+R'))
+ '%s\t%s' % (_("Transformation"), _('Alt+R'))
)
self.geo_editor_menu.addSeparator()
self.geo_cornersnap_menuitem = self.geo_editor_menu.addAction(
diff --git a/appPlugins/ToolTransform.py b/appPlugins/ToolTransform.py
index d057ceef..70402a63 100644
--- a/appPlugins/ToolTransform.py
+++ b/appPlugins/ToolTransform.py
@@ -78,7 +78,7 @@ class ToolTransform(AppTool):
AppTool.run(self)
self.set_tool_ui()
- self.app.ui.notebook.setTabText(2, _("Object Transform"))
+ self.app.ui.notebook.setTabText(2, _("Transformation"))
def install(self, icon=None, separator=None, **kwargs):
AppTool.install(self, icon, separator, shortcut='Alt+T', **kwargs)
@@ -560,11 +560,11 @@ class ToolTransform(AppTool):
class TransformUI:
- pluginName = _("Object Transform")
+ pluginName = _("Transformation")
rotateName = _("Rotate")
skewName = _("Skew/Shear")
scaleName = _("Scale")
- flipName = _("Mirror (Flip)")
+ flipName = _("Mirror")
offsetName = _("Offset")
bufferName = _("Buffer")