Files
flatcam-wsl/appPlugins/ToolFollow.py
Marius Stanciu ff805880e1 - fixed the Follow plugin not marking correctly the resulting Geometry objet as a multigeo
- fixed the Cutout plugin not working with Geometry objects that are made out of a MultiLineString
2022-01-25 22:31:50 +02:00

826 lines
34 KiB
Python

# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# File by: Marius Adrian Stanciu (c) #
# Date: 11/12/2020 #
# License: MIT Licence #
# ##########################################################
from PyQt6 import QtWidgets, QtCore, QtGui
from appTool import AppTool
from appGUI.GUIElements import RadioSet, FCButton, FCComboBox, FCLabel, VerticalScrollArea, FCGridLayout, FCFrame
from appParsers.ParseGerber import Gerber
from camlib import flatten_shapely_geometry
from copy import deepcopy
import numpy as np
from shapely.ops import unary_union
from shapely.geometry import Polygon
from matplotlib.backend_bases import KeyEvent as mpl_key_event
import logging
import gettext
import appTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
log = logging.getLogger('base')
class ToolFollow(AppTool, Gerber):
optimal_found_sig = QtCore.pyqtSignal(float)
def __init__(self, app):
self.app = app
self.decimals = self.app.decimals
AppTool.__init__(self, app)
Gerber.__init__(self, steps_per_circle=self.app.defaults["gerber_circle_steps"])
# #############################################################################
# ######################### Tool GUI ##########################################
# #############################################################################
self.ui = FollowUI(layout=self.layout, app=self.app)
self.pluginName = self.ui.pluginName
self.connect_signals_at_init()
# disconnect flags
self.area_sel_disconnect_flag = False
self.first_click = False
self.cursor_pos = None
self.mouse_is_dragging = False
self.mm = None
self.mp = None
self.mr = None
self.kp = None
self.sel_rect = []
# store here the points for the "Polygon" area selection shape
self.points = []
# set this as True when in middle of drawing a "Polygon" area selection shape
# it is made False by first click to signify that the shape is complete
self.poly_drawn = False
def install(self, icon=None, separator=None, **kwargs):
AppTool.install(self, icon, separator, shortcut='', **kwargs)
def run(self, toggle=True):
self.app.defaults.report_usage("ToolFollow()")
if toggle:
# if the splitter is 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)
try:
if self.app.ui.plugin_scroll_area.widget().objectName() == self.pluginName and found_idx:
# if the Tool Tab is not focused, focus on it
if not self.app.ui.notebook.currentWidget() is self.app.ui.plugin_tab:
# focus on Tool Tab
self.app.ui.notebook.setCurrentWidget(self.app.ui.plugin_tab)
else:
# else remove the Tool Tab
self.app.ui.notebook.setCurrentWidget(self.app.ui.properties_tab)
self.app.ui.notebook.removeTab(2)
# if there are no objects loaded in the app then hide the Notebook widget
if not self.app.collection.get_list():
self.app.ui.splitter.setSizes([0, 1])
except AttributeError:
pass
else:
if self.app.ui.splitter.sizes()[0] == 0:
self.app.ui.splitter.setSizes([1, 1])
AppTool.run(self)
self.set_tool_ui()
self.app.ui.notebook.setTabText(2, _("Follow"))
def connect_signals_at_init(self):
self.ui.level.toggled.connect(self.on_level_changed)
self.ui.selectmethod_radio.activated_custom.connect(self.ui.on_selection)
self.ui.generate_geometry_button.clicked.connect(self.on_generate_geometry_click)
def set_tool_ui(self):
self.units = self.app.app_units.upper()
self.clear_ui(self.layout)
self.ui = FollowUI(layout=self.layout, app=self.app)
self.pluginName = self.ui.pluginName
self.connect_signals_at_init()
self.ui.selectmethod_radio.set_value('all') # _("All")
self.ui.area_shape_radio.set_value('square')
self.sel_rect[:] = []
self.points = []
self.poly_drawn = False
self.area_sel_disconnect_flag = False
# SELECT THE CURRENT OBJECT
obj = self.app.collection.get_active()
if obj and obj.kind == 'gerber':
obj_name = obj.options['name']
self.ui.object_combo.set_value(obj_name)
# Show/Hide Advanced Options
app_mode = self.app.defaults["global_app_level"]
self.change_level(app_mode)
def change_level(self, level):
"""
:param level: application level: either 'b' or 'a'
:type level: str
:return:
"""
if level == 'a':
self.ui.level.setChecked(True)
else:
self.ui.level.setChecked(False)
self.on_level_changed(self.ui.level.isChecked())
def on_level_changed(self, checked):
if not checked:
self.ui.level.setText('%s' % _('Beginner'))
self.ui.level.setStyleSheet("""
QToolButton
{
color: green;
}
""")
# Parameters section
self.ui.gp_frame.hide()
self.ui.param_label.hide()
else:
self.ui.level.setText('%s' % _('Advanced'))
self.ui.level.setStyleSheet("""
QToolButton
{
color: red;
}
""")
# Parameters section
self.ui.gp_frame.show()
self.ui.param_label.show()
def on_generate_geometry_click(self):
obj_name = self.ui.object_combo.currentText()
# Get source object.
try:
obj = self.app.collection.get_by_name(obj_name)
except Exception as e:
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), str(obj_name)))
return "Could not retrieve object: %s with error: %s" % (obj_name, str(e))
if obj is None:
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), str(obj_name)))
return
formatted_name = obj_name.rpartition('.')[0]
if formatted_name == '':
formatted_name = obj_name
outname = '%s_follow' % formatted_name
select_method = self.ui.selectmethod_radio.get_value()
if select_method == 'all': # _("All")
self.follow_all(obj, outname)
else:
# disable the "notebook UI" until finished
self.app.ui.notebook.setDisabled(True)
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the start point of the area."))
if self.app.use_3d_engine:
self.app.plotcanvas.graph_event_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
self.app.plotcanvas.graph_event_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
self.app.plotcanvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
else:
self.app.plotcanvas.graph_event_disconnect(self.app.mp)
self.app.plotcanvas.graph_event_disconnect(self.app.mm)
self.app.plotcanvas.graph_event_disconnect(self.app.mr)
self.mr = self.app.plotcanvas.graph_event_connect('mouse_release', self.on_mouse_release)
self.mm = self.app.plotcanvas.graph_event_connect('mouse_move', self.on_mouse_move)
self.kp = self.app.plotcanvas.graph_event_connect('key_press', self.on_key_press)
# disconnect flags
self.area_sel_disconnect_flag = True
def follow_all(self, obj, outname):
def job_thread(tool_obj):
tool_obj.follow_geo(obj, outname)
self.app.worker_task.emit({'fcn': job_thread, 'params': [self]})
def follow_area(self):
obj_name = self.ui.object_combo.currentText()
# Get source object.
try:
obj = self.app.collection.get_by_name(obj_name)
except Exception as e:
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), str(obj_name)))
return "Could not retrieve object: %s with error: %s" % (obj_name, str(e))
if obj is None:
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), str(self.obj_name)))
return
formatted_name = obj_name.rpartition('.')[0]
if formatted_name == '':
formatted_name = obj_name
outname = '%s_follow' % formatted_name
def job_thread(tool_obj):
tool_obj.follow_geo_area(obj, outname)
self.app.worker_task.emit({'fcn': job_thread, 'params': [self]})
def follow_geo(self, followed_obj, outname):
"""
Creates a geometry object "following" the gerber paths.
:param followed_obj: Gerber object for which to generate the follow geometry
:type followed_obj: AppObjects.FlatCAMGerber.GerberObject
:param outname: Nme of the resulting Geometry object
:type outname: str
:return: None
"""
def follow_init(new_obj, app_obj):
if type(app_obj.defaults["tools_mill_tooldia"]) == float:
tools_list = [app_obj.defaults["tools_mill_tooldia"]]
else:
try:
temp_tools = app_obj.defaults["tools_mill_tooldia"].split(",")
tools_list = [
float(eval(dia)) for dia in temp_tools if dia != ''
]
except Exception as e:
log.error("ToolFollow.follow_geo -> At least one tool diameter needed. -> %s" % str(e))
return 'fail'
# store here the default data for Geometry Data
new_data = {}
for opt_key in app_obj.options:
if opt_key.find('geometry' + "_") == 0:
oname = opt_key[len('geometry') + 1:]
new_data[oname] = app_obj.options[opt_key]
if opt_key.find('tools_') == 0:
new_data[opt_key] = app_obj.options[opt_key]
followed_obj.follow_geometry = flatten_shapely_geometry(followed_obj.follow_geometry)
follow_geo = [
g for g in followed_obj.follow_geometry if g and not g.is_empty and g.is_valid and
g.geom_type != 'Point'
]
if not follow_geo:
self.app.log.warning("ToolFollow.follow_geo() -> Empty Follow Geometry")
return 'fail'
new_obj.multigeo = True
# Propagate options
new_obj.options["tools_mill_tooldia"] = app_obj.defaults["tools_mill_tooldia"]
new_obj.solid_geometry = follow_geo
new_obj.tools = {
1: {
'tooldia': app_obj.dec_format(float(tools_list[0]), self.decimals),
'data': deepcopy(new_data),
'solid_geometry': new_obj.solid_geometry
}
}
ret = self.app.app_obj.new_object("geometry", outname, follow_init)
if ret == 'fail':
self.app.inform.emit("[ERROR_NOTCL] %s" % _("Failed to create Follow Geometry."))
else:
self.app.inform.emit("[success] %s" % _("Done."))
def follow_geo_area(self, followed_obj, outname):
"""
Creates a geometry object "following" the gerber paths.
:param followed_obj: Gerber object for which to generate the follow geometry
:type followed_obj: AppObjects.FlatCAMGerber.GerberObject
:param outname: Nme of the resulting Geometry object
:type outname: str
:return: None
"""
def follow_init(new_obj, app_obj):
new_obj.multigeo = True
if type(app_obj.defaults["tools_mill_tooldia"]) == float:
tools_list = [app_obj.defaults["tools_mill_tooldia"]]
else:
try:
temp_tools = app_obj.defaults["tools_mill_tooldia"].split(",")
tools_list = [
float(eval(dia)) for dia in temp_tools if dia != ''
]
except Exception as e:
log.error("ToolFollow.follow_geo -> At least one tool diameter needed. -> %s" % str(e))
return 'fail'
# store here the default data for Geometry Data
new_data = {}
for opt_key, opt_val in app_obj.options.items():
if opt_key.find('geometry' + "_") == 0:
oname = opt_key[len('geometry') + 1:]
new_data[oname] = app_obj.options[opt_key]
if opt_key.find('tools_') == 0:
new_data[opt_key] = app_obj.options[opt_key]
# Propagate options
new_obj.options["tools_mill_tooldia"] = app_obj.defaults["tools_mill_tooldia"]
new_data["tools_mill_tooldia"] = app_obj.defaults["tools_mill_tooldia"]
target_geo = unary_union(followed_obj.follow_geometry)
area_follow = target_geo.intersection(deepcopy(unary_union(self.sel_rect)))
self.sel_rect[:] = []
self.points = []
area_follow = flatten_shapely_geometry(area_follow)
cleaned_area_follow = [g for g in area_follow if not g.is_empty and g.is_valid and g.geom_type != 'Point']
new_obj.multigeo = True
new_obj.solid_geometry = deepcopy(cleaned_area_follow)
new_obj.tools = {
1: {
'tooldia': app_obj.dec_format(float(tools_list[0]), self.decimals),
'offset': 'Path',
'offset_value': 0.0,
'type': 'Rough',
'tool_type': 'C1',
'data': deepcopy(new_data),
'solid_geometry': new_obj.solid_geometry
}
}
ret = self.app.app_obj.new_object("geometry", outname, follow_init)
if ret == 'fail':
self.app.inform.emit("[ERROR_NOTCL] %s" % _("Failed to create Follow Geometry."))
else:
self.app.inform.emit("[success] %s" % _("Done."))
# To be called after clicking on the plot.
def on_mouse_release(self, event):
if self.app.use_3d_engine:
event_pos = event.pos
right_button = 2
else:
event_pos = (event.xdata, event.ydata)
right_button = 3
try:
x = float(event_pos[0])
y = float(event_pos[1])
except TypeError:
return
event_pos = (x, y)
shape_type = self.ui.area_shape_radio.get_value()
curr_pos = self.app.plotcanvas.translate_coords(event_pos)
if self.app.grid_status():
curr_pos = self.app.geo_editor.snap(curr_pos[0], curr_pos[1])
x1, y1 = curr_pos[0], curr_pos[1]
# do paint single only for left mouse clicks
if event.button == 1:
if shape_type == "square":
if not self.first_click:
self.first_click = True
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the end point of the area."))
self.cursor_pos = self.app.plotcanvas.translate_coords(event_pos)
if self.app.grid_status():
self.cursor_pos = self.app.geo_editor.snap(self.cursor_pos[0], self.cursor_pos[1])
else:
self.app.inform.emit(_("Zone added. Click to start adding next zone or right click to finish."))
self.app.delete_selection_shape()
x0, y0 = self.cursor_pos[0], self.cursor_pos[1]
pt1 = (x0, y0)
pt2 = (x1, y0)
pt3 = (x1, y1)
pt4 = (x0, y1)
new_rectangle = Polygon([pt1, pt2, pt3, pt4])
self.sel_rect.append(new_rectangle)
# add a temporary shape on canvas
self.draw_tool_selection_shape(old_coords=(x0, y0), coords=(x1, y1))
self.first_click = False
return
else:
self.points.append((x1, y1))
if len(self.points) > 1:
self.poly_drawn = True
self.app.inform.emit(_("Click on next Point or click right mouse button to complete ..."))
return ""
elif event.button == right_button and self.mouse_is_dragging is False:
shape_type = self.ui.area_shape_radio.get_value()
if shape_type == "square":
self.first_click = False
else:
# if we finish to add a polygon
if self.poly_drawn is True:
try:
# try to add the point where we last clicked if it is not already in the self.points
last_pt = (x1, y1)
if last_pt != self.points[-1]:
self.points.append(last_pt)
except IndexError:
pass
# we need to add a Polygon and a Polygon can be made only from at least 3 points
if len(self.points) > 2:
self.delete_moving_selection_shape()
pol = Polygon(self.points)
# do not add invalid polygons even if they are drawn by utility geometry
if pol.is_valid:
self.sel_rect.append(pol)
self.draw_selection_shape_polygon(points=self.points)
self.app.inform.emit(
_("Zone added. Click to start adding next zone or right click to finish."))
self.points = []
self.poly_drawn = False
return
self.delete_tool_selection_shape()
if self.app.use_3d_engine:
self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_release)
self.app.plotcanvas.graph_event_disconnect('mouse_move', self.on_mouse_move)
self.app.plotcanvas.graph_event_disconnect('key_press', self.on_key_press)
else:
self.app.plotcanvas.graph_event_disconnect(self.mr)
self.app.plotcanvas.graph_event_disconnect(self.mm)
self.app.plotcanvas.graph_event_disconnect(self.kp)
self.app.mp = self.app.plotcanvas.graph_event_connect('mouse_press',
self.app.on_mouse_click_over_plot)
self.app.mm = self.app.plotcanvas.graph_event_connect('mouse_move',
self.app.on_mouse_move_over_plot)
self.app.mr = self.app.plotcanvas.graph_event_connect('mouse_release',
self.app.on_mouse_click_release_over_plot)
# disconnect flags
self.area_sel_disconnect_flag = False
# disable the "notebook UI" until finished
self.app.ui.notebook.setDisabled(False)
if len(self.sel_rect) == 0:
return
self.follow_area()
# called on mouse move
def on_mouse_move(self, event):
shape_type = self.ui.area_shape_radio.get_value()
if self.app.use_3d_engine:
event_pos = event.pos
event_is_dragging = event.is_dragging
# right_button = 2
else:
event_pos = (event.xdata, event.ydata)
event_is_dragging = self.app.plotcanvas.is_dragging
# right_button = 3
try:
x = float(event_pos[0])
y = float(event_pos[1])
except TypeError:
return
curr_pos = self.app.plotcanvas.translate_coords((x, y))
# detect mouse dragging motion
if event_is_dragging == 1:
self.mouse_is_dragging = True
else:
self.mouse_is_dragging = False
# update the cursor position
if self.app.grid_status():
# Update cursor
curr_pos = self.app.geo_editor.snap(curr_pos[0], curr_pos[1])
self.app.app_cursor.set_data(np.asarray([(curr_pos[0], curr_pos[1])]),
symbol='++', edge_color=self.app.cursor_color_3D,
edge_width=self.app.defaults["global_cursor_width"],
size=self.app.defaults["global_cursor_size"])
if self.cursor_pos is None:
self.cursor_pos = (0, 0)
self.app.dx = curr_pos[0] - float(self.cursor_pos[0])
self.app.dy = curr_pos[1] - float(self.cursor_pos[1])
# # update the positions on status bar
# self.app.ui.position_label.setText("&nbsp;<b>X</b>: %.4f&nbsp;&nbsp; "
# "<b>Y</b>: %.4f&nbsp;" % (curr_pos[0], curr_pos[1]))
# self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp; <b>Dy</b>: "
# "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (self.app.dx, self.app.dy))
self.app.ui.update_location_labels(self.app.dx, self.app.dy, curr_pos[0], curr_pos[1])
units = self.app.app_units.lower()
# self.app.plotcanvas.text_hud.text = \
# 'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\n\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format(
# self.app.dx, units, self.app.dy, units, curr_pos[0], units, curr_pos[1], units)
self.app.plotcanvas.on_update_text_hud(self.app.dx, self.app.dy, curr_pos[0], curr_pos[1])
# draw the utility geometry
if shape_type == "square":
if self.first_click:
self.app.delete_selection_shape()
self.app.draw_moving_selection_shape(old_coords=(self.cursor_pos[0], self.cursor_pos[1]),
coords=(curr_pos[0], curr_pos[1]))
else:
self.delete_moving_selection_shape()
self.draw_moving_selection_shape_poly(points=self.points, data=(curr_pos[0], curr_pos[1]))
def on_key_press(self, event):
# modifiers = QtWidgets.QApplication.keyboardModifiers()
# matplotlib_key_flag = False
# events out of the self.app.collection view (it's about Project Tab) are of type int
if type(event) is int:
key = event
# events from the GUI are of type QKeyEvent
elif type(event) == QtGui.QKeyEvent:
key = event.key()
elif isinstance(event, mpl_key_event): # MatPlotLib key events are trickier to interpret than the rest
# matplotlib_key_flag = True
key = event.key
key = QtGui.QKeySequence(key)
# check for modifiers
key_string = key.toString().lower()
if '+' in key_string:
mod, __, key_text = key_string.rpartition('+')
if mod.lower() == 'ctrl':
# modifiers = QtCore.Qt.KeyboardModifier.ControlModifier
pass
elif mod.lower() == 'alt':
# modifiers = QtCore.Qt.KeyboardModifier.AltModifier
pass
elif mod.lower() == 'shift':
# modifiers = QtCore.Qt.KeyboardModifier.
pass
else:
# modifiers = QtCore.Qt.KeyboardModifier.NoModifier
pass
key = QtGui.QKeySequence(key_text)
# events from Vispy are of type KeyEvent
else:
key = event.key
if key == QtCore.Qt.Key.Key_Escape or key == 'Escape':
if self.area_sel_disconnect_flag is True:
try:
if self.app.use_3d_engine:
self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_release)
self.app.plotcanvas.graph_event_disconnect('mouse_move', self.on_mouse_move)
self.app.plotcanvas.graph_event_disconnect('key_press', self.on_key_press)
else:
self.app.plotcanvas.graph_event_disconnect(self.mr)
self.app.plotcanvas.graph_event_disconnect(self.mm)
self.app.plotcanvas.graph_event_disconnect(self.kp)
except Exception as e:
log.error("ToolFollow.on_key_press() _1 --> %s" % str(e))
self.app.mp = self.app.plotcanvas.graph_event_connect('mouse_press',
self.app.on_mouse_click_over_plot)
self.app.mm = self.app.plotcanvas.graph_event_connect('mouse_move',
self.app.on_mouse_move_over_plot)
self.app.mr = self.app.plotcanvas.graph_event_connect('mouse_release',
self.app.on_mouse_click_release_over_plot)
self.points = []
self.poly_drawn = False
self.sel_rect[:] = []
self.delete_moving_selection_shape()
self.delete_tool_selection_shape()
# disable the "notebook UI" until finished
self.app.ui.notebook.setDisabled(False)
class FollowUI:
pluginName = _("Follow")
def __init__(self, layout, app):
self.app = app
self.decimals = self.app.decimals
self.layout = layout
self.tools_frame = QtWidgets.QFrame()
self.tools_frame.setContentsMargins(0, 0, 0, 0)
self.layout.addWidget(self.tools_frame)
self.tools_box = QtWidgets.QVBoxLayout()
self.tools_box.setContentsMargins(0, 0, 0, 0)
self.tools_frame.setLayout(self.tools_box)
self.title_box = QtWidgets.QHBoxLayout()
self.tools_box.addLayout(self.title_box)
# ## Title
title_label = FCLabel("%s" % self.pluginName)
title_label.setStyleSheet("""
QLabel
{
font-size: 16px;
font-weight: bold;
}
""")
title_label.setToolTip(
_("Create a Geometry object with\n"
"toolpaths to cut through the middle of polygons.")
)
self.title_box.addWidget(title_label)
# App Level label
self.level = QtWidgets.QToolButton()
self.level.setToolTip(
_(
"Beginner Mode - many parameters are hidden.\n"
"Advanced Mode - full control.\n"
"Permanent change is done in 'Preferences' menu."
)
)
self.level.setCheckable(True)
self.title_box.addWidget(self.level)
# #############################################################################################################
# ################################ The object to be followed ##################################################
# #############################################################################################################
self.obj_combo_label = FCLabel('<span style="color:darkorange;"><b>%s</b></span>' % _("Source Object"))
self.obj_combo_label.setToolTip(
_("A Gerber object to be followed.\n"
"Create a Geometry object with a path\n"
"following the Gerber traces.")
)
self.tools_box.addWidget(self.obj_combo_label)
self.object_combo = FCComboBox()
self.object_combo.setModel(self.app.collection)
self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.object_combo.is_last = True
self.tools_box.addWidget(self.object_combo)
# #############################################################################################################
# COMMON PARAMETERS Frame
# #############################################################################################################
self.param_label = FCLabel('<span style="color:blue;"><b>%s</b></span>' % _("Parameters"))
self.param_label.setToolTip(_("Parameters that are common for all tools."))
self.tools_box.addWidget(self.param_label)
self.gp_frame = FCFrame()
self.tools_box.addWidget(self.gp_frame)
grid0 = FCGridLayout(v_spacing=5, h_spacing=3)
self.gp_frame.setLayout(grid0)
# Polygon selection
self.select_label = FCLabel('%s:' % _('Selection'))
self.select_label.setToolTip(
_("Selection of area to be processed.\n"
"- 'All Polygons' - the process will start after click.\n"
"- 'Area Selection' - left mouse click to start selection of the area to be processed.")
)
self.selectmethod_radio = RadioSet([{'label': _("All"), 'value': 'all'},
{'label': _("Area Selection"), 'value': 'area'}])
grid0.addWidget(self.select_label, 0, 0)
grid0.addWidget(self.selectmethod_radio, 0, 1)
# Area Selection shape
self.area_shape_label = FCLabel('%s:' % _("Shape"))
self.area_shape_label.setToolTip(
_("The kind of selection shape used for area selection.")
)
self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'},
{'label': _("Polygon"), 'value': 'polygon'}])
grid0.addWidget(self.area_shape_label, 2, 0)
grid0.addWidget(self.area_shape_radio, 2, 1)
self.area_shape_label.hide()
self.area_shape_radio.hide()
self.generate_geometry_button = FCButton("%s" % _("Generate Geometry"))
self.generate_geometry_button.setIcon(QtGui.QIcon(self.app.resource_location + '/geometry32.png'))
self.generate_geometry_button.setStyleSheet("""
QPushButton
{
font-weight: bold;
}
""")
self.generate_geometry_button.setToolTip(_("Generate a 'Follow' geometry.\n"
"This means that it will cut through\n"
"the middle of the trace."))
self.tools_box.addWidget(self.generate_geometry_button)
self.tools_box.addStretch(1)
# ## Reset Tool
self.reset_button = FCButton(_("Reset Tool"))
self.reset_button.setIcon(QtGui.QIcon(self.app.resource_location + '/reset32.png'))
self.reset_button.setToolTip(
_("Will reset the tool parameters.")
)
self.reset_button.setStyleSheet("""
QPushButton
{
font-weight: bold;
}
""")
self.tools_box.addWidget(self.reset_button)
# ############################ FINISHED GUI ###################################
# #############################################################################
def on_selection(self, val):
if val == 'area': # _("Area Selection")
self.area_shape_label.show()
self.area_shape_radio.show()
else: # All
self.area_shape_label.hide()
self.area_shape_radio.hide()
def confirmation_message(self, accepted, minval, maxval):
if accepted is False:
self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' % (_("Edited value is out of range"),
self.decimals,
minval,
self.decimals,
maxval), False)
else:
self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
def confirmation_message_int(self, accepted, minval, maxval):
if accepted is False:
self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' %
(_("Edited value is out of range"), minval, maxval), False)
else:
self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)