Added feature: Select all polygons for painting and shell support with "paint" command.

This commit is contained in:
Juan Pablo Caram
2016-09-19 16:44:29 -04:00
parent 74e936ba02
commit 7474609776
7 changed files with 242 additions and 35 deletions

View File

@@ -259,6 +259,7 @@ class App(QtCore.QObject):
"geometry_spindlespeed": self.defaults_form.geometry_group.cncspindlespeed_entry,
"geometry_paintoverlap": self.defaults_form.geometry_group.paintoverlap_entry,
"geometry_paintmargin": self.defaults_form.geometry_group.paintmargin_entry,
"geometry_selectmethod": self.defaults_form.geometry_group.selectmethod_combo,
"cncjob_plot": self.defaults_form.cncjob_group.plot_cb,
"cncjob_tooldia": self.defaults_form.cncjob_group.tooldia_entry,
"cncjob_prepend": self.defaults_form.cncjob_group.prepend_text,
@@ -304,6 +305,7 @@ class App(QtCore.QObject):
"geometry_painttooldia": 0.07,
"geometry_paintoverlap": 0.15,
"geometry_paintmargin": 0.0,
"geometry_selectmethod": "single",
"cncjob_plot": True,
"cncjob_tooldia": 0.016,
"cncjob_prepend": "",
@@ -397,6 +399,7 @@ class App(QtCore.QObject):
"geometry_painttooldia": self.options_form.geometry_group.painttooldia_entry,
"geometry_paintoverlap": self.options_form.geometry_group.paintoverlap_entry,
"geometry_paintmargin": self.options_form.geometry_group.paintmargin_entry,
"geometry_selectmethod": self.options_form.geometry_group.selectmethod_combo,
"cncjob_plot": self.options_form.cncjob_group.plot_cb,
"cncjob_tooldia": self.options_form.cncjob_group.tooldia_entry,
"cncjob_prepend": self.options_form.cncjob_group.prepend_text,
@@ -439,12 +442,15 @@ class App(QtCore.QObject):
"geometry_painttooldia": 0.07,
"geometry_paintoverlap": 0.15,
"geometry_paintmargin": 0.0,
"geometry_selectmethod": "single",
"cncjob_plot": True,
"cncjob_tooldia": 0.016,
"cncjob_prepend": "",
"cncjob_append": "",
"background_timeout": 300000, #default value is 5 minutes
"verbose_error_level": 0, # shell verbosity 0 = default(python trace only for unknown errors), 1 = show trace(show trace allways), 2 = (For the future).
"background_timeout": 300000, # Default value is 5 minutes
"verbose_error_level": 0, # Shell verbosity:
# 0 = default(python trace only for unknown errors),
# 1 = show trace(show trace allways), 2 = (For the future).
})
self.options.update(self.defaults) # Copy app defaults to project options
#self.options_write_form()
@@ -877,13 +883,13 @@ class App(QtCore.QObject):
#self.shell.append_error("?\n")
self.shell.append_error(str(e) + "\n")
def info(self, msg):
def info(self, msg, toshell=True):
"""
Informs the user. Normally on the status bar, optionally
also on the shell.
:param msg: Text to write.
:param toshell: Forward the
:param toshell: Forward the meesage to the shell.
:return: None
"""
@@ -894,12 +900,15 @@ class App(QtCore.QObject):
msg_ = match.group(2)
self.ui.fcinfo.set_status(QtCore.QString(msg_), level=level)
error = level == "error" or level == "warning"
self.shell_message(msg, error=error, show=True)
if toshell:
error = level == "error" or level == "warning"
self.shell_message(msg, error=error, show=True)
else:
self.ui.fcinfo.set_status(QtCore.QString(msg), level="info")
self.shell_message(msg)
if toshell:
self.shell_message(msg)
def load_defaults(self):
"""
@@ -981,6 +990,11 @@ class App(QtCore.QObject):
this is, updates the GUI accordingly, any other records and plots it.
This method is thread-safe.
Notes:
* If the name is in use, the self.collection will modify it
when appending it to the collection. There is no need to handle
name conflicts here.
:param kind: The kind of object to create. One of 'gerber',
'excellon', 'cncjob' and 'geometry'.
:type kind: str
@@ -998,19 +1012,6 @@ class App(QtCore.QObject):
t0 = time.time() # Debug
### Check for existing name
# while name in self.collection.get_names():
# ## Create a new name
# # Ends with number?
# App.log.debug("new_object(): Object name (%s) exists, changing." % name)
# match = re.search(r'(.*[^\d])?(\d+)$', name)
# if match: # Yes: Increment the number!
# base = match.group(1) or ''
# num = int(match.group(2))
# name = base + str(num + 1)
# else: # No: add a number!
# name += "_1"
## Create object
classdict = {
"gerber": FlatCAMGerber,

View File

@@ -746,7 +746,21 @@ class GeometryOptionsGroupUI(OptionsGroupUI):
)
grid2.addWidget(marginlabel, 2, 0)
self.paintmargin_entry = LengthEntry()
grid2.addWidget(self.paintmargin_entry)
grid2.addWidget(self.paintmargin_entry, 2, 1)
# Polygon selection
selectlabel = QtGui.QLabel('Selection:')
selectlabel.setToolTip(
"How to select the polygons to paint."
)
grid2.addWidget(selectlabel, 3, 0)
# grid3 = QtGui.QGridLayout()
self.selectmethod_combo = RadioSet([
{"label": "Single", "value": "single"},
{"label": "All", "value": "all"},
# {"label": "Rectangle", "value": "rectangle"}
])
grid2.addWidget(self.selectmethod_combo, 3, 1)
class CNCJobOptionsGroupUI(OptionsGroupUI):

View File

@@ -1203,7 +1203,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
"paintmargin": 0.01,
"paintmethod": "standard",
"multidepth": False,
"depthperpass": 0.002
"depthperpass": 0.002,
"selectmethod": "single"
})
# Attributes to be included in serialization
@@ -1234,7 +1235,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
"paintmargin": self.ui.paintmargin_entry,
"paintmethod": self.ui.paintmethod_combo,
"multidepth": self.ui.mpass_cb,
"depthperpass": self.ui.maxdepth_entry
"depthperpass": self.ui.maxdepth_entry,
"selectmethod": self.ui.selectmethod_combo
})
self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click)
@@ -1244,24 +1246,38 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
def on_paint_button_click(self, *args):
self.app.report_usage("geometry_on_paint_button")
self.app.info("Click inside the desired polygon.")
self.read_form()
tooldia = self.options["painttooldia"]
overlap = self.options["paintoverlap"]
# Connection ID for the click event
subscription = None
if self.options["selectmethod"] == "all":
self.paint_poly_all(tooldia, overlap)
return
# To be called after clicking on the plot.
def doit(event):
self.app.info("Painting polygon...")
self.app.plotcanvas.mpl_disconnect(subscription)
point = [event.xdata, event.ydata]
self.paint_poly(point, tooldia, overlap)
if self.options["selectmethod"] == "single":
self.app.info("Click inside the desired polygon.")
subscription = self.app.plotcanvas.mpl_connect('button_press_event', doit)
# To be called after clicking on the plot.
def doit(event):
self.app.info("Painting polygon...")
self.app.plotcanvas.mpl_disconnect(subscription)
point = [event.xdata, event.ydata]
self.paint_poly_single_click(point, tooldia, overlap)
def paint_poly(self, inside_pt, tooldia, overlap):
subscription = self.app.plotcanvas.mpl_connect('button_press_event', doit)
def paint_poly_single_click(self, inside_pt, tooldia, overlap, outname=None):
"""
Paints a polygon selected by clicking on its interior.
Note:
* The margin is taken directly from the form.
:param inside_pt: [x, y]
:param tooldia: Diameter of the painting tool
:param overlap: Overlap of the tool between passes.
:return: None
"""
# Which polygon.
#poly = find_polygon(self.solid_geometry, inside_pt)
@@ -1275,7 +1291,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
proc = self.app.proc_container.new("Painting polygon.")
name = self.options["name"] + "_paint"
name = outname or self.options["name"] + "_paint"
# Initializes the new geometry object
def gen_paintarea(geo_obj, app_obj):
@@ -1293,6 +1309,73 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
geo_obj.solid_geometry = list(cp.get_objects())
geo_obj.options["cnctooldia"] = tooldia
# Experimental...
print "Indexing...",
geo_obj.make_index()
print "Done"
self.app.inform.emit("Done.")
def job_thread(app_obj):
try:
app_obj.new_object("geometry", name, gen_paintarea)
except Exception as e:
proc.done()
raise e
proc.done()
self.app.inform.emit("Polygon Paint started ...")
# Promise object with the new name
self.app.collection.promise(name)
# Background
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
def paint_poly_all(self, tooldia, overlap, outname=None):
proc = self.app.proc_container.new("Painting polygon.")
name = outname or self.options["name"] + "_paint"
def recurse(geo):
try:
for subg in geo:
for subsubg in recurse(subg):
yield subsubg
except TypeError:
if isinstance(geo, Polygon):
yield geo
raise StopIteration
# Initializes the new geometry object
def gen_paintarea(geo_obj, app_obj):
assert isinstance(geo_obj, FlatCAMGeometry), \
"Initializer expected a FlatCAMGeometry, got %s" % type(geo_obj)
geo_obj.solid_geometry = []
for poly in recurse(self.solid_geometry):
if self.options["paintmethod"] == "seed":
cp = self.clear_polygon2(poly.buffer(-self.options["paintmargin"]),
tooldia, overlap=overlap)
else:
cp = self.clear_polygon(poly.buffer(-self.options["paintmargin"]),
tooldia, overlap=overlap)
geo_obj.solid_geometry += list(cp.get_objects())
geo_obj.options["cnctooldia"] = tooldia
# Experimental...
print "Indexing...",
geo_obj.make_index()
print "Done"
self.app.inform.emit("Done.")
def job_thread(app_obj):

View File

@@ -397,6 +397,20 @@ class GeometryObjectUI(ObjectUI):
])
grid2.addWidget(self.paintmethod_combo, 3, 1)
# Polygon selection
selectlabel = QtGui.QLabel('Selection:')
selectlabel.setToolTip(
"How to select the polygons to paint."
)
grid2.addWidget(selectlabel, 4, 0)
#grid3 = QtGui.QGridLayout()
self.selectmethod_combo = RadioSet([
{"label": "Single", "value": "single"},
{"label": "All", "value": "all"},
#{"label": "Rectangle", "value": "rectangle"}
])
grid2.addWidget(self.selectmethod_combo, 4, 1)
# GO Button
self.generate_paint_button = QtGui.QPushButton('Generate')
self.generate_paint_button.setToolTip(

View File

@@ -92,6 +92,16 @@ class Geometry(object):
# Flattened geometry (list of paths only)
self.flat_geometry = []
# Index
self.index = None
def make_index(self):
self.flatten()
self.index = FlatCAMRTree()
for i, g in enumerate(self.flat_geometry):
self.index.insert(i, g)
def add_circle(self, origin, radius):
"""
Adds a circle to the object.

View File

@@ -0,0 +1,84 @@
from ObjectCollection import *
import TclCommand
class TclCommandPaint(TclCommand.TclCommandSignaled):
"""
Paint the interior of polygons
"""
# Array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['paint']
# dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('name', str),
('tooldia', float),
('overlap', float)
])
# dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value
option_types = collections.OrderedDict([
('outname', str),
('all', bool),
('x', float),
('y', float)
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
required = ['name', 'tooldia', 'overlap']
# structured help for current command, args needs to be ordered
help = {
'main': "Paint polygons",
'args': collections.OrderedDict([
('name', 'Name of the source Geometry object.'),
('tooldia', 'Diameter of the tool to be used.'),
('overlap', 'Fraction of the tool diameter to overlap cuts.'),
('outname', 'Name of the resulting Geometry object.'),
('all', 'Paint all polygons in the object.'),
('x', 'X value of coordinate for the selection of a single polygon.'),
('y', 'Y value of coordinate for the selection of a single polygon.')
]),
'examples': []
}
def execute(self, args, unnamed_args):
"""
execute current TCL shell command
:param args: array of known named arguments and options
:param unnamed_args: array of other values which were passed into command
without -somename and we do not have them in known arg_names
:return: None or exception
"""
name = args['name']
tooldia = args['tooldia']
overlap = args['overlap']
if 'outname' in args:
outname = args['outname']
else:
outname = name + "_paint"
obj = self.app.collection.get_by_name(name)
if obj is None:
self.raise_tcl_error("Object not found: %s" % name)
if not isinstance(obj, Geometry):
self.raise_tcl_error('Expected Geometry, got %s %s.' % (name, type(obj)))
if 'all' in args and args['all']:
obj.paint_poly_all(tooldia, overlap, outname)
return
if 'x' not in args or 'y' not in args:
self.raise_tcl_error('Expected -all 1 or -x <value> and -y <value>.')
x = args['x']
y = args['y']
obj.paint_poly_single_click([x, y], tooldia, overlap, outname)

View File

@@ -13,6 +13,7 @@ import tclCommands.TclCommandInteriors
import tclCommands.TclCommandIsolate
import tclCommands.TclCommandNew
import tclCommands.TclCommandOpenGerber
import tclCommands.TclCommandPaint
__all__ = []