Changed project name to FlatCAM.

This commit is contained in:
Juan Pablo Caram 2014-02-01 14:23:31 -05:00
parent 8bcaa65529
commit e930de1793
3 changed files with 206 additions and 105 deletions

View File

@ -13,11 +13,11 @@ import sys
########################################
## CirkuixObj ##
## FlatCAMObj ##
########################################
class CirkuixObj:
class FlatCAMObj:
"""
Base type of objects handled in Cirkuix. These become interactive
Base type of objects handled in FlatCAM. These become interactive
in the GUI, can be plotted, and their options can be modified
by the user in their respective forms.
"""
@ -178,25 +178,25 @@ class CirkuixObj:
def deserialize(self, obj_dict):
"""
Re-builds an object from its serialized version.
@param obj_dict: Dictionary representing a CirkuixObj
@param obj_dict: Dictionary representing a FlatCAMObj
@type obj_dict: dict
@return None
"""
return
class CirkuixGerber(CirkuixObj, Gerber):
class FlatCAMGerber(FlatCAMObj, Gerber):
"""
Represents Gerber code.
"""
def __init__(self, name):
Gerber.__init__(self)
CirkuixObj.__init__(self, name)
FlatCAMObj.__init__(self, name)
self.kind = "gerber"
# The 'name' is already in self.options from CirkuixObj
# The 'name' is already in self.options from FlatCAMObj
self.options.update({
"plot": True,
"mergepolys": True,
@ -211,7 +211,7 @@ class CirkuixGerber(CirkuixObj, Gerber):
"bboxrounded": False
})
# The 'name' is already in self.form_kinds from CirkuixObj
# The 'name' is already in self.form_kinds from FlatCAMObj
self.form_kinds.update({
"plot": "cb",
"mergepolys": "cb",
@ -235,6 +235,15 @@ class CirkuixGerber(CirkuixObj, Gerber):
self.ser_attrs += ['options', 'kind']
def convert_units(self, units):
"""
Converts the units of the object by scaling dimensions in all geometry
and options.
:param units: Units to which to convert the object: "IN" or "MM".
:type units: str
:return: None
:rtype: None
"""
factor = Gerber.convert_units(self, units)
self.options['isotooldia'] *= factor
@ -244,7 +253,7 @@ class CirkuixGerber(CirkuixObj, Gerber):
self.options['bboxmargin'] *= factor
def plot(self, figure):
CirkuixObj.plot(self, figure)
FlatCAMObj.plot(self, figure)
self.create_geometry()
@ -276,14 +285,14 @@ class CirkuixGerber(CirkuixObj, Gerber):
}
class CirkuixExcellon(CirkuixObj, Excellon):
class FlatCAMExcellon(FlatCAMObj, Excellon):
"""
Represents Excellon code.
"""
def __init__(self, name):
Excellon.__init__(self)
CirkuixObj.__init__(self, name)
FlatCAMObj.__init__(self, name)
self.kind = "excellon"
@ -323,7 +332,7 @@ class CirkuixExcellon(CirkuixObj, Excellon):
self.options['feedrate'] *= factor
def plot(self, figure):
CirkuixObj.plot(self, figure)
FlatCAMObj.plot(self, figure)
#self.setup_axes(figure)
self.create_geometry()
@ -344,7 +353,7 @@ class CirkuixExcellon(CirkuixObj, Excellon):
box.set_orientation(Gtk.Orientation(1))
win.add(box)
for tool in self.tools:
self.tool_cbs[tool] = Gtk.CheckButton(label=tool + ": " + self.tools[tool])
self.tool_cbs[tool] = Gtk.CheckButton(label=tool + ": " + str(self.tools[tool]))
box.pack_start(self.tool_cbs[tool], False, False, 1)
button = Gtk.Button(label="Accept")
box.pack_start(button, False, False, 1)
@ -363,7 +372,7 @@ class CirkuixExcellon(CirkuixObj, Excellon):
button.connect("clicked", on_accept)
class CirkuixCNCjob(CirkuixObj, CNCjob):
class FlatCAMCNCjob(FlatCAMObj, CNCjob):
"""
Represents G-Code.
"""
@ -372,7 +381,7 @@ class CirkuixCNCjob(CirkuixObj, CNCjob):
feedrate=3.0, z_cut=-0.002, tooldia=0.0):
CNCjob.__init__(self, units=units, kind=kind, z_move=z_move,
feedrate=feedrate, z_cut=z_cut, tooldia=tooldia)
CirkuixObj.__init__(self, name)
FlatCAMObj.__init__(self, name)
self.kind = "cncjob"
@ -396,21 +405,25 @@ class CirkuixCNCjob(CirkuixObj, CNCjob):
self.ser_attrs += ['options', 'kind']
def plot(self, figure):
CirkuixObj.plot(self, figure)
FlatCAMObj.plot(self, figure)
#self.setup_axes(figure)
self.plot2(self.axes, tooldia=self.options["tooldia"])
self.app.on_zoom_fit(None)
self.app.canvas.queue_draw()
def convert_units(self, units):
factor = CNCjob.convert_units(self, units)
print "FlatCAMCNCjob.convert_units()"
self.options["tooldia"] *= factor
class CirkuixGeometry(CirkuixObj, Geometry):
class FlatCAMGeometry(FlatCAMObj, Geometry):
"""
Geometric object not associated with a specific
format.
"""
def __init__(self, name):
CirkuixObj.__init__(self, name)
FlatCAMObj.__init__(self, name)
Geometry.__init__(self)
self.kind = "geometry"
@ -467,7 +480,7 @@ class CirkuixGeometry(CirkuixObj, Geometry):
return factor
def plot(self, figure):
CirkuixObj.plot(self, figure)
FlatCAMObj.plot(self, figure)
#self.setup_axes(figure)
try:
@ -525,11 +538,11 @@ class App:
GObject.threads_init()
## GUI ##
self.gladefile = "cirkuix.ui"
self.gladefile = "FlatCAM.ui"
self.builder = Gtk.Builder()
self.builder.add_from_file(self.gladefile)
self.window = self.builder.get_object("window1")
self.window.set_title("Cirkuix")
self.window.set_title("FlatCAM")
self.position_label = self.builder.get_object("label3")
self.grid = self.builder.get_object("grid1")
self.notebook = self.builder.get_object("notebook1")
@ -560,7 +573,7 @@ class App:
#### DATA ####
self.setup_obj_classes()
self.stuff = {} # CirkuixObj's by name
self.stuff = {} # FlatCAMObj's by name
self.mouse = None # Mouse coordinates over plot
# What is selected by the user. It is
@ -571,6 +584,8 @@ class App:
# the options are being changed by the program and not the user.
self.options_update_ignore = False
self.toggle_units_ignore = False
self.defaults = {
"units": "in"
} # Application defaults
@ -590,10 +605,10 @@ class App:
# self.combos = []
# Options for each kind of CirkuixObj.
# Options for each kind of FlatCAMObj.
# Example: 'gerber_plot': 'cb'. The widget name would be: 'cb_app_gerber_plot'
for CirkuixClass in [CirkuixExcellon, CirkuixGeometry, CirkuixGerber, CirkuixCNCjob]:
obj = CirkuixClass("no_name")
for FlatCAMClass in [FlatCAMExcellon, FlatCAMGeometry, FlatCAMGerber, FlatCAMCNCjob]:
obj = FlatCAMClass("no_name")
for option in obj.form_kinds:
self.form_kinds[obj.kind + "_" + option] = obj.form_kinds[option]
# if obj.form_kinds[option] == "radio":
@ -657,11 +672,11 @@ class App:
def setup_obj_classes(self):
"""
Sets up application specifics on the CirkuixObj class.
Sets up application specifics on the FlatCAMObj class.
:return: None
"""
CirkuixObj.app = self
FlatCAMObj.app = self
def setup_project_list(self):
"""
@ -873,7 +888,7 @@ class App:
def new_object(self, kind, name, initialize):
"""
Creates a new specalized CirkuixObj and attaches it to the application,
Creates a new specalized FlatCAMObj and attaches it to the application,
this is, updates the GUI accordingly, any other records and plots it.
:param kind: The kind of object to create. One of 'gerber',
@ -896,12 +911,13 @@ class App:
# Create object
classdict = {
"gerber": CirkuixGerber,
"excellon": CirkuixExcellon,
"cncjob": CirkuixCNCjob,
"geometry": CirkuixGeometry
"gerber": FlatCAMGerber,
"excellon": FlatCAMExcellon,
"cncjob": FlatCAMCNCjob,
"geometry": FlatCAMGeometry
}
obj = classdict[kind](name)
obj.units = self.options["units"] # TODO: The constructor should look at defaults.
# Initialize as per user request
# User must take care to implement initialize
@ -958,10 +974,10 @@ class App:
def get_current(self):
"""
Returns the currently selected CirkuixObj in the application.
Returns the currently selected FlatCAMObj in the application.
:return: Currently selected CirkuixObj in the application.
:rtype: CirkuixObj or None
:return: Currently selected FlatCAMObj in the application.
:rtype: FlatCAMObj or None
"""
try:
return self.stuff[self.selected_item_name]
@ -1095,6 +1111,7 @@ class App:
# Set the on-change callback to do nothing while we do the changes.
self.options_update_ignore = True
self.toggle_units_ignore = True
combo_sel = self.combo_options.get_active()
options_set = [self.options, self.defaults][combo_sel]
@ -1102,6 +1119,7 @@ class App:
self.set_form_item(option, options_set[option])
self.options_update_ignore = False
self.toggle_units_ignore = False
def set_form_item(self, name, value):
"""
@ -1201,6 +1219,7 @@ class App:
# Project options
self.options.update(d['options'])
self.project_filename = filename
self.units_label.set_text(self.options["units"])
# Re create objects
for obj in d['objs']:
@ -1213,6 +1232,76 @@ class App:
########################################
## EVENT HANDLERS ##
########################################
def on_toggle_units(self, widget):
if self.toggle_units_ignore:
return
combo_sel = self.combo_options.get_active()
options_set = [self.options, self.defaults][combo_sel]
# Options to scale
dimensions = ['gerber_isotooldia', 'gerber_cutoutmargin', 'gerber_cutoutgapsize',
'gerber_noncoppermargin', 'gerber_bboxmargin', 'excellon_drillz',
'excellon_travelz', 'excellon_feedrate', 'cncjob_tooldia',
'geometry_cutz', 'geometry_travelz', 'geometry_feedrate',
'geometry_cnctooldia', 'geometry_painttooldia', 'geometry_paintoverlap',
'geometry_paintmargin']
def scale_options(factor):
for dim in dimensions:
options_set[dim] *= factor
factor = 1/25.4
if self.builder.get_object('rb_mm').get_active():
factor = 25.4
# App units. Convert without warning.
if combo_sel == 1:
self.read_form()
scale_options(factor)
self.options2form()
return
label = Gtk.Label("Changing the units of the project causes all geometrical \n" + \
"properties of all objects to be scaled accordingly. Continue?")
dialog = Gtk.Dialog("Changing Project Units", self.window, 0,
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_OK, Gtk.ResponseType.OK))
dialog.set_default_size(150, 100)
dialog.set_modal(True)
box = dialog.get_content_area()
box.set_border_width(10)
box.add(label)
dialog.show_all()
response = dialog.run()
dialog.destroy()
if response == Gtk.ResponseType.OK:
print "Converting units..."
print "Converting options..."
self.read_form()
scale_options(factor)
self.options2form()
for obj in self.stuff:
units = self.get_radio_value({"rb_mm": "MM", "rb_inch": "IN"})
print "Converting ", obj, " to ", units
self.stuff[obj].convert_units(units)
current = self.get_current()
if current is not None:
current.to_form()
self.plot_all()
else:
# Undo toggling
self.toggle_units_ignore = True
if self.builder.get_object('rb_mm').get_active():
self.builder.get_object('rb_inch').set_active(True)
else:
self.builder.get_object('rb_mm').set_active(True)
self.toggle_units_ignore = False
self.read_form()
self.units_label.set_text("[" + self.options["units"] + "]")
def on_file_openproject(self, param):
"""
Callback for menu item File->Open Project. Opens a file chooser and calls
@ -1443,7 +1532,7 @@ class App:
:return: None
"""
obj = self.get_current()
assert isinstance(obj, CirkuixObj)
assert isinstance(obj, FlatCAMObj)
factor = self.get_eval("entry_eval_" + obj.kind + "_scalefactor")
obj.scale(factor)
obj.to_form()
@ -1480,7 +1569,7 @@ class App:
def on_generate_gerber_bounding_box(self, widget):
"""
Callback for request from the Gerber form to generate a bounding box for the
geometry in the object. Creates a CirkuixGeometry with the bounding box.
geometry in the object. Creates a FlatCAMGeometry with the bounding box.
:param widget: Ignored.
:return: None
@ -1490,7 +1579,7 @@ class App:
name = self.selected_item_name + "_bbox"
def geo_init(geo_obj, app_obj):
assert isinstance(geo_obj, CirkuixGeometry)
assert isinstance(geo_obj, FlatCAMGeometry)
bounding_box = gerber.solid_geometry.envelope.buffer(gerber.options["bboxmargin"])
if not gerber.options["bboxrounded"]:
bounding_box = bounding_box.envelope
@ -1535,14 +1624,14 @@ class App:
job_name = self.selected_item_name + "_cnc"
excellon = self.get_current()
assert isinstance(excellon, CirkuixExcellon)
assert isinstance(excellon, FlatCAMExcellon)
excellon.read_form()
# Object initialization function for app.new_object()
def job_init(job_obj, app_obj):
excellon_ = self.get_current()
assert isinstance(excellon_, CirkuixExcellon)
assert isinstance(job_obj, CirkuixCNCjob)
assert isinstance(excellon_, FlatCAMExcellon)
assert isinstance(job_obj, FlatCAMCNCjob)
GLib.idle_add(lambda: app_obj.set_progress_bar(0.2, "Creating CNC Job..."))
job_obj.z_cut = excellon_.options["drillz"]
@ -1581,7 +1670,7 @@ class App:
:return: None
"""
excellon = self.get_current()
assert isinstance(excellon, CirkuixExcellon)
assert isinstance(excellon, FlatCAMExcellon)
excellon.show_tool_chooser()
def on_entry_eval_activate(self, widget):
@ -1595,7 +1684,7 @@ class App:
"""
self.on_eval_update(widget)
obj = self.get_current()
assert isinstance(obj, CirkuixObj)
assert isinstance(obj, FlatCAMObj)
obj.read_form()
def on_gerber_generate_noncopper(self, widget):
@ -1610,9 +1699,9 @@ class App:
name = self.selected_item_name + "_noncopper"
def geo_init(geo_obj, app_obj):
assert isinstance(geo_obj, CirkuixGeometry)
assert isinstance(geo_obj, FlatCAMGeometry)
gerber = app_obj.stuff[app_obj.selected_item_name]
assert isinstance(gerber, CirkuixGerber)
assert isinstance(gerber, FlatCAMGerber)
gerber.read_form()
bounding_box = gerber.solid_geometry.envelope.buffer(gerber.options["noncoppermargin"])
non_copper = bounding_box.difference(gerber.solid_geometry)
@ -1713,9 +1802,9 @@ class App:
# Object initialization function for app.new_object()
def job_init(job_obj, app_obj):
assert isinstance(job_obj, CirkuixCNCjob)
assert isinstance(job_obj, FlatCAMCNCjob)
geometry = app_obj.stuff[app_obj.selected_item_name]
assert isinstance(geometry, CirkuixGeometry)
assert isinstance(geometry, FlatCAMGeometry)
geometry.read_form()
GLib.idle_add(lambda: app_obj.set_progress_bar(0.2, "Creating CNC Job..."))
@ -1752,7 +1841,7 @@ class App:
Subscribes to the "Click on plot" event and continues
after the click. Finds the polygon containing
the clicked point and runs clear_poly() on it, resulting
in a new CirkuixGeometry object.
in a new FlatCAMGeometry object.
:param widget: The widget from which this was called.
:return: None
@ -1772,7 +1861,7 @@ class App:
# Initializes the new geometry object
def gen_paintarea(geo_obj, app_obj):
assert isinstance(geo_obj, CirkuixGeometry)
assert isinstance(geo_obj, FlatCAMGeometry)
assert isinstance(app_obj, App)
cp = clear_poly(poly.buffer(-geo.options["paintmargin"]), tooldia, overlap)
geo_obj.solid_geometry = cp
@ -1800,7 +1889,7 @@ class App:
def on_delete(self, widget):
"""
Delete the currently selected CirkuixObj.
Delete the currently selected FlatCAMObj.
:param widget: The widget from which this was called.
:return: None
@ -1862,7 +1951,7 @@ class App:
def on_tree_selection_changed(self, selection):
"""
Callback for selection change in the project list. This changes
the currently selected CirkuixObj.
the currently selected FlatCAMObj.
:param selection: Selection associated to the project tree or list
:type selection: Gtk.TreeSelection
@ -1991,7 +2080,7 @@ class App:
def on_fileopengerber(self, param):
"""
Callback for menu item File->Open Gerber. Defines a function that is then passed
to ``self.file_chooser_action()``. It requests the creation of a CirkuixGerber object
to ``self.file_chooser_action()``. It requests the creation of a FlatCAMGerber object
and updates the progress bar throughout the process.
:param param: Ignore
@ -2021,7 +2110,7 @@ class App:
def on_fileopenexcellon(self, param):
"""
Callback for menu item File->Open Excellon. Defines a function that is then passed
to ``self.file_chooser_action()``. It requests the creation of a CirkuixExcellon object
to ``self.file_chooser_action()``. It requests the creation of a FlatCAMExcellon object
and updates the progress bar throughout the process.
:param param: Ignore
@ -2051,7 +2140,7 @@ class App:
def on_fileopengcode(self, param):
"""
Callback for menu item File->Open G-Code. Defines a function that is then passed
to ``self.file_chooser_action()``. It requests the creation of a CirkuixCNCjob object
to ``self.file_chooser_action()``. It requests the creation of a FlatCAMCNCjob object
and updates the progress bar throughout the process.
:param param: Ignore
@ -2113,8 +2202,9 @@ class App:
by the Matplotlib backend and has been registered in ``self.__init__()``.
For details, see: http://matplotlib.org/users/event_handling.html
:param event:
:return:
:param event: Contains information about the event, like which button
was clicked, the pixel coordinates and the axes coordinates.
:return: None
"""
# For key presses
self.canvas.grab_focus()

View File

@ -2546,6 +2546,7 @@ to application defaults.</property>
<property name="xalign">0</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_toggle_units" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>

108
camlib.py
View File

@ -45,6 +45,7 @@ class Geometry:
return (0, 0, 0, 0)
if type(self.solid_geometry) == list:
# TODO: This can be done faster. See comment from Shapely mailing lists.
return cascaded_union(self.solid_geometry).bounds
else:
return self.solid_geometry.bounds
@ -98,13 +99,15 @@ class Geometry:
def convert_units(self, units):
"""
Converts the units of the object to ``units`` by scaling all
the geometry appropriately.
the geometry appropriately. This call ``scale()``. Don't call
it again in descendents.
:param units: "IN" or "MM"
:type units: str
:return: Scaling factor resulting from unit change.
:rtype: float
"""
print "Geometry.convert_units()"
if units.upper() == self.units.upper():
return 1.0
@ -578,6 +581,7 @@ class Excellon(Geometry):
"""
Scales geometry on the XY plane in the object by a given factor.
Tool sizes, feedrates an Z-plane dimensions are untouched.
:param factor: Number by which to scale the object.
:type factor: float
:return: None
@ -586,7 +590,7 @@ class Excellon(Geometry):
# Drills
for drill in self.drills:
drill.point = affinity.scale(drill.point, factor, factor, origin=(0, 0))
drill['point'] = affinity.scale(drill['point'], factor, factor, origin=(0, 0))
def convert_units(self, units):
factor = Geometry.convert_units(self, units)
@ -639,7 +643,18 @@ class CNCjob(Geometry):
self.ser_attrs += ['kind', 'z_cut', 'z_move', 'feedrate', 'tooldia',
'gcode', 'input_geometry_bounds', 'gcode_parsed',
'steps_per_circ']
def convert_units(self, units):
factor = Geometry.convert_units(self, units)
print "CNCjob.convert_units()"
self.z_cut *= factor
self.z_move *= factor
self.feedrate *= factor
self.tooldia *= factor
return factor
def generate_from_excellon(self, exobj):
"""
Generates G-code for drilling from Excellon object.
@ -897,44 +912,44 @@ class CNCjob(Geometry):
self.gcode_parsed = geometry
return geometry
def plot(self, tooldia=None, dpi=75, margin=0.1,
color={"T": ["#F0E24D", "#B5AB3A"], "C": ["#5E6CFF", "#4650BD"]},
alpha={"T": 0.3, "C": 1.0}):
"""
Creates a Matplotlib figure with a plot of the
G-code job.
"""
if tooldia is None:
tooldia = self.tooldia
fig = Figure(dpi=dpi)
ax = fig.add_subplot(111)
ax.set_aspect(1)
xmin, ymin, xmax, ymax = self.input_geometry_bounds
ax.set_xlim(xmin-margin, xmax+margin)
ax.set_ylim(ymin-margin, ymax+margin)
if tooldia == 0:
for geo in self.gcode_parsed:
linespec = '--'
linecolor = color[geo['kind'][0]][1]
if geo['kind'][0] == 'C':
linespec = 'k-'
x, y = geo['geom'].coords.xy
ax.plot(x, y, linespec, color=linecolor)
else:
for geo in self.gcode_parsed:
poly = geo['geom'].buffer(tooldia/2.0)
patch = PolygonPatch(poly, facecolor=color[geo['kind'][0]][0],
edgecolor=color[geo['kind'][0]][1],
alpha=alpha[geo['kind'][0]], zorder=2)
ax.add_patch(patch)
return fig
# def plot(self, tooldia=None, dpi=75, margin=0.1,
# color={"T": ["#F0E24D", "#B5AB3A"], "C": ["#5E6CFF", "#4650BD"]},
# alpha={"T": 0.3, "C": 1.0}):
# """
# Creates a Matplotlib figure with a plot of the
# G-code job.
# """
# if tooldia is None:
# tooldia = self.tooldia
#
# fig = Figure(dpi=dpi)
# ax = fig.add_subplot(111)
# ax.set_aspect(1)
# xmin, ymin, xmax, ymax = self.input_geometry_bounds
# ax.set_xlim(xmin-margin, xmax+margin)
# ax.set_ylim(ymin-margin, ymax+margin)
#
# if tooldia == 0:
# for geo in self.gcode_parsed:
# linespec = '--'
# linecolor = color[geo['kind'][0]][1]
# if geo['kind'][0] == 'C':
# linespec = 'k-'
# x, y = geo['geom'].coords.xy
# ax.plot(x, y, linespec, color=linecolor)
# else:
# for geo in self.gcode_parsed:
# poly = geo['geom'].buffer(tooldia/2.0)
# patch = PolygonPatch(poly, facecolor=color[geo['kind'][0]][0],
# edgecolor=color[geo['kind'][0]][1],
# alpha=alpha[geo['kind'][0]], zorder=2)
# ax.add_patch(patch)
#
# return fig
def plot2(self, axes, tooldia=None, dpi=75, margin=0.1,
color={"T": ["#F0E24D", "#B5AB3A"], "C": ["#5E6CFF", "#4650BD"]},
alpha={"T": 0.3, "C":1.0}):
alpha={"T": 0.3, "C": 1.0}):
"""
Plots the G-code job onto the given axes.
"""
@ -964,8 +979,9 @@ class CNCjob(Geometry):
"""
Creates G-Code for the exterior and all interior paths
of a polygon.
@param polygon: A Shapely.Polygon
@type polygon: Shapely.Polygon
:param polygon: A Shapely.Polygon
:type polygon: Shapely.Polygon
"""
gcode = ""
t = "G0%d X%.4fY%.4f\n"
@ -1009,23 +1025,17 @@ class CNCjob(Geometry):
Scales all the geometry on the XY plane in the object by the
given factor. Tool sizes, feedrates, or Z-axis dimensions are
not altered.
:param factor: Number by which to scale the object.
:type factor: float
:return: None
:rtype: None
"""
for g in self.gcode_parsed:
g['geom'] = affinity.scale(g['geom'], factor, factor, origin=(0, 0))
def convert_units(self, units):
factor = Geometry.convert_units(self, units)
self.z_move *= factor
self.z_cut *= factor
self.feedrate *= factor
self.tooldia *= factor
return factor
self.create_geometry()
def get_bounds(geometry_set):