Major modifications to data/gui interactions. In progress.
This commit is contained in:
parent
a20a7e1d8c
commit
0bdc3b19f0
2965
FlatCAM.py
2965
FlatCAM.py
File diff suppressed because it is too large
Load Diff
4011
FlatCAM.ui
4011
FlatCAM.ui
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
495
FlatCAMObj.py
495
FlatCAMObj.py
|
@ -11,7 +11,32 @@ from gi.repository import Gdk
|
|||
from gi.repository import GLib
|
||||
from gi.repository import GObject
|
||||
|
||||
import inspect # TODO: Remove
|
||||
|
||||
from FlatCAMApp import *
|
||||
from camlib import *
|
||||
from ObjectUI import *
|
||||
|
||||
|
||||
class LoudDict(dict):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(LoudDict, self).__init__(*args, **kwargs)
|
||||
self.callback = lambda x: None
|
||||
self.silence = False
|
||||
|
||||
def set_change_callback(self, callback):
|
||||
if self.silence:
|
||||
return
|
||||
self.callback = callback
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
super(LoudDict, self).__setitem__(key, value)
|
||||
try:
|
||||
if self.__getitem__(key) == value:
|
||||
return
|
||||
except KeyError:
|
||||
pass
|
||||
self.callback(key)
|
||||
|
||||
|
||||
########################################
|
||||
|
@ -28,19 +53,63 @@ class FlatCAMObj(GObject.GObject, object):
|
|||
# The app should set this value.
|
||||
app = None
|
||||
|
||||
def __init__(self, name):
|
||||
# name = GObject.property(type=str)
|
||||
|
||||
def __init__(self, name, ui):
|
||||
"""
|
||||
|
||||
:param name: Name of the object given by the user.
|
||||
:param ui: User interface to interact with the object.
|
||||
:type ui: ObjectUI
|
||||
:return: FlatCAMObj
|
||||
"""
|
||||
GObject.GObject.__init__(self)
|
||||
|
||||
self.options = {"name": name}
|
||||
self.form_kinds = {"name": "entry_text"} # Kind of form element for each option
|
||||
# View
|
||||
self.ui = ui
|
||||
|
||||
self.options = LoudDict(name=name)
|
||||
self.options.set_change_callback(self.on_options_change)
|
||||
|
||||
self.form_fields = {"name": self.ui.name_entry}
|
||||
self.radios = {} # Name value pairs for radio sets
|
||||
self.radios_inv = {} # Inverse of self.radios
|
||||
self.axes = None # Matplotlib axes
|
||||
self.kind = None # Override with proper name
|
||||
|
||||
self.muted_ui = False
|
||||
|
||||
self.ui.name_entry.connect('activate', self.on_name_activate)
|
||||
self.ui.offset_button.connect('clicked', self.on_offset_button_click)
|
||||
self.ui.offset_button.connect('activate', self.on_offset_button_click)
|
||||
self.ui.scale_button.connect('clicked', self.on_scale_button_click)
|
||||
self.ui.scale_button.connect('activate', self.on_scale_button_click)
|
||||
|
||||
def __str__(self):
|
||||
return "<FlatCAMObj({:12s}): {:20s}>".format(self.kind, self.options["name"])
|
||||
|
||||
def on_name_activate(self, *args):
|
||||
old_name = copy(self.options["name"])
|
||||
new_name = self.ui.name_entry.get_text()
|
||||
self.options["name"] = self.ui.name_entry.get_text()
|
||||
self.app.info("Name changed from %s to %s" % (old_name, new_name))
|
||||
|
||||
def on_offset_button_click(self, *args):
|
||||
self.read_form()
|
||||
vect = self.ui.offsetvector_entry.get_value()
|
||||
self.offset(vect)
|
||||
self.plot()
|
||||
|
||||
def on_scale_button_click(self, *args):
|
||||
self.read_form()
|
||||
factor = self.ui.scale_entry.get_value()
|
||||
self.scale(factor)
|
||||
self.plot()
|
||||
|
||||
def on_options_change(self, key):
|
||||
self.form_fields[key].set_value(self.options[key])
|
||||
return
|
||||
|
||||
def setup_axes(self, figure):
|
||||
"""
|
||||
1) Creates axes if they don't exist. 2) Clears axes. 3) Attaches
|
||||
|
@ -89,6 +158,7 @@ class FlatCAMObj(GObject.GObject, object):
|
|||
:return: None
|
||||
:rtype: None
|
||||
"""
|
||||
print inspect.stack()[1][3], "--> FlatCAMObj.read_form()"
|
||||
for option in self.options:
|
||||
self.read_form_item(option)
|
||||
|
||||
|
@ -100,27 +170,25 @@ class FlatCAMObj(GObject.GObject, object):
|
|||
:rtype: None
|
||||
"""
|
||||
|
||||
self.muted_ui = True
|
||||
print inspect.stack()[1][3], "--> FlatCAMObj.build_ui()"
|
||||
|
||||
# Where the UI for this object is drawn
|
||||
box_selected = self.app.builder.get_object("box_selected")
|
||||
# box_selected = self.app.builder.get_object("box_selected")
|
||||
box_selected = self.app.builder.get_object("vp_selected")
|
||||
|
||||
# Remove anything else in the box
|
||||
box_children = box_selected.get_children()
|
||||
for child in box_children:
|
||||
box_selected.remove(child)
|
||||
|
||||
osw = self.app.builder.get_object("offscrwindow_" + self.kind) # offscreenwindow
|
||||
sw = self.app.builder.get_object("sw_" + self.kind) # scrollwindows
|
||||
osw.remove(sw) # TODO: Is this needed ?
|
||||
vp = self.app.builder.get_object("vp_" + self.kind) # Viewport
|
||||
vp.override_background_color(Gtk.StateType.NORMAL, Gdk.RGBA(1, 1, 1, 1))
|
||||
|
||||
# Put in the UI
|
||||
box_selected.pack_start(sw, True, True, 0)
|
||||
|
||||
# entry_name = self.app.builder.get_object("entry_text_" + self.kind + "_name")
|
||||
# entry_name.connect("activate", self.app.on_activate_name)
|
||||
# box_selected.pack_start(sw, True, True, 0)
|
||||
box_selected.add(self.ui)
|
||||
self.to_form()
|
||||
sw.show()
|
||||
box_selected.show_all()
|
||||
self.ui.show()
|
||||
self.muted_ui = False
|
||||
|
||||
def set_form_item(self, option):
|
||||
"""
|
||||
|
@ -130,19 +198,11 @@ class FlatCAMObj(GObject.GObject, object):
|
|||
:type option: str
|
||||
:return: None
|
||||
"""
|
||||
fkind = self.form_kinds[option]
|
||||
fname = fkind + "_" + self.kind + "_" + option
|
||||
|
||||
if fkind == 'entry_eval' or fkind == 'entry_text':
|
||||
self.app.builder.get_object(fname).set_text(str(self.options[option]))
|
||||
return
|
||||
if fkind == 'cb':
|
||||
self.app.builder.get_object(fname).set_active(self.options[option])
|
||||
return
|
||||
if fkind == 'radio':
|
||||
self.app.builder.get_object(self.radios_inv[option][self.options[option]]).set_active(True)
|
||||
return
|
||||
print "Unknown kind of form item:", fkind
|
||||
try:
|
||||
self.form_fields[option].set_value(self.options[option])
|
||||
except KeyError:
|
||||
App.log.warn("Tried to set an option or field that does not exist: %s" % option)
|
||||
|
||||
def read_form_item(self, option):
|
||||
"""
|
||||
|
@ -152,22 +212,11 @@ class FlatCAMObj(GObject.GObject, object):
|
|||
:type option: str
|
||||
:return: None
|
||||
"""
|
||||
fkind = self.form_kinds[option]
|
||||
fname = fkind + "_" + self.kind + "_" + option
|
||||
|
||||
if fkind == 'entry_text':
|
||||
self.options[option] = self.app.builder.get_object(fname).get_text()
|
||||
return
|
||||
if fkind == 'entry_eval':
|
||||
self.options[option] = self.app.get_eval(fname)
|
||||
return
|
||||
if fkind == 'cb':
|
||||
self.options[option] = self.app.builder.get_object(fname).get_active()
|
||||
return
|
||||
if fkind == 'radio':
|
||||
self.options[option] = self.app.get_radio_value(self.radios[option])
|
||||
return
|
||||
print "Unknown kind of form item:", fkind
|
||||
try:
|
||||
self.options[option] = self.form_fields[option].get_value()
|
||||
except KeyError:
|
||||
App.log.warning("Failed to read option from field: %s" % option)
|
||||
|
||||
def plot(self):
|
||||
"""
|
||||
|
@ -221,14 +270,31 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
|
||||
def __init__(self, name):
|
||||
Gerber.__init__(self)
|
||||
FlatCAMObj.__init__(self, name)
|
||||
FlatCAMObj.__init__(self, name, GerberObjectUI())
|
||||
|
||||
self.kind = "gerber"
|
||||
|
||||
self.form_fields.update({
|
||||
"plot": self.ui.plot_cb,
|
||||
"multicolored": self.ui.multicolored_cb,
|
||||
"solid": self.ui.solid_cb,
|
||||
"isotooldia": self.ui.iso_tool_dia_entry,
|
||||
"isopasses": self.ui.iso_width_entry,
|
||||
"isooverlap": self.ui.iso_overlap_entry,
|
||||
"cutouttooldia": self.ui.cutout_tooldia_entry,
|
||||
"cutoutmargin": self.ui.cutout_margin_entry,
|
||||
"cutoutgapsize": self.ui.cutout_gap_entry,
|
||||
"gaps": self.ui.gaps_radio,
|
||||
"noncoppermargin": self.ui.noncopper_margin_entry,
|
||||
"noncopperrounded": self.ui.noncopper_rounded_cb,
|
||||
"bboxmargin": self.ui.bbmargin_entry,
|
||||
"bboxrounded": self.ui.bbrounded_cb
|
||||
})
|
||||
|
||||
# The 'name' is already in self.options from FlatCAMObj
|
||||
# Automatically updates the UI
|
||||
self.options.update({
|
||||
"plot": True,
|
||||
"mergepolys": True,
|
||||
"multicolored": False,
|
||||
"solid": False,
|
||||
"isotooldia": 0.016,
|
||||
|
@ -244,33 +310,137 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
"bboxrounded": False
|
||||
})
|
||||
|
||||
# The 'name' is already in self.form_kinds from FlatCAMObj
|
||||
self.form_kinds.update({
|
||||
"plot": "cb",
|
||||
"mergepolys": "cb",
|
||||
"multicolored": "cb",
|
||||
"solid": "cb",
|
||||
"isotooldia": "entry_eval",
|
||||
"isopasses": "entry_eval",
|
||||
"isooverlap": "entry_eval",
|
||||
"cutouttooldia": "entry_eval",
|
||||
"cutoutmargin": "entry_eval",
|
||||
"cutoutgapsize": "entry_eval",
|
||||
"gaps": "radio",
|
||||
"noncoppermargin": "entry_eval",
|
||||
"noncopperrounded": "cb",
|
||||
"bboxmargin": "entry_eval",
|
||||
"bboxrounded": "cb"
|
||||
})
|
||||
|
||||
self.radios = {"gaps": {"rb_2tb": "tb", "rb_2lr": "lr", "rb_4": "4"}}
|
||||
self.radios_inv = {"gaps": {"tb": "rb_2tb", "lr": "rb_2lr", "4": "rb_4"}}
|
||||
|
||||
# Attributes to be included in serialization
|
||||
# Always append to it because it carries contents
|
||||
# from predecessors.
|
||||
self.ser_attrs += ['options', 'kind']
|
||||
|
||||
assert isinstance(self.ui, GerberObjectUI)
|
||||
self.ui.plot_cb.connect('clicked', self.on_plot_cb_click)
|
||||
self.ui.plot_cb.connect('activate', self.on_plot_cb_click)
|
||||
self.ui.solid_cb.connect('clicked', self.on_solid_cb_click)
|
||||
self.ui.solid_cb.connect('activate', self.on_solid_cb_click)
|
||||
self.ui.multicolored_cb.connect('clicked', self.on_multicolored_cb_click)
|
||||
self.ui.multicolored_cb.connect('activate', self.on_multicolored_cb_click)
|
||||
self.ui.generate_iso_button.connect('clicked', self.on_iso_button_click)
|
||||
self.ui.generate_iso_button.connect('activate', self.on_iso_button_click)
|
||||
self.ui.generate_cutout_button.connect('clicked', self.on_generatecutout_button_click)
|
||||
self.ui.generate_cutout_button.connect('activate', self.on_generatecutout_button_click)
|
||||
self.ui.generate_bb_button.connect('clicked', self.on_generatebb_button_click)
|
||||
self.ui.generate_bb_button.connect('activate', self.on_generatebb_button_click)
|
||||
self.ui.generate_noncopper_button.connect('clicked', self.on_generatenoncopper_button_click)
|
||||
self.ui.generate_noncopper_button.connect('activate', self.on_generatenoncopper_button_click)
|
||||
|
||||
def on_generatenoncopper_button_click(self, *args):
|
||||
self.read_form()
|
||||
name = self.options["name"] + "_noncopper"
|
||||
|
||||
def geo_init(geo_obj, app_obj):
|
||||
assert isinstance(geo_obj, FlatCAMGeometry)
|
||||
bounding_box = self.solid_geometry.envelope.buffer(self.options["noncoppermargin"])
|
||||
if not self.options["noncopperrounded"]:
|
||||
bounding_box = bounding_box.envelope
|
||||
non_copper = bounding_box.difference(self.solid_geometry)
|
||||
geo_obj.solid_geometry = non_copper
|
||||
|
||||
# TODO: Check for None
|
||||
self.app.new_object("geometry", name, geo_init)
|
||||
|
||||
def on_generatebb_button_click(self, *args):
|
||||
self.read_form()
|
||||
name = self.options["name"] + "_bbox"
|
||||
|
||||
def geo_init(geo_obj, app_obj):
|
||||
assert isinstance(geo_obj, FlatCAMGeometry)
|
||||
# Bounding box with rounded corners
|
||||
bounding_box = self.solid_geometry.envelope.buffer(self.options["bboxmargin"])
|
||||
if not self.options["bboxrounded"]: # Remove rounded corners
|
||||
bounding_box = bounding_box.envelope
|
||||
geo_obj.solid_geometry = bounding_box
|
||||
|
||||
self.app.new_object("geometry", name, geo_init)
|
||||
|
||||
def on_generatecutout_button_click(self, *args):
|
||||
self.read_form()
|
||||
name = self.options["name"] + "_cutout"
|
||||
|
||||
def geo_init(geo_obj, app_obj):
|
||||
margin = self.options["cutoutmargin"] + self.options["cutouttooldia"]/2
|
||||
gap_size = self.options["cutoutgapsize"] + self.options["cutouttooldia"]
|
||||
minx, miny, maxx, maxy = self.bounds()
|
||||
minx -= margin
|
||||
maxx += margin
|
||||
miny -= margin
|
||||
maxy += margin
|
||||
midx = 0.5 * (minx + maxx)
|
||||
midy = 0.5 * (miny + maxy)
|
||||
hgap = 0.5 * gap_size
|
||||
pts = [[midx - hgap, maxy],
|
||||
[minx, maxy],
|
||||
[minx, midy + hgap],
|
||||
[minx, midy - hgap],
|
||||
[minx, miny],
|
||||
[midx - hgap, miny],
|
||||
[midx + hgap, miny],
|
||||
[maxx, miny],
|
||||
[maxx, midy - hgap],
|
||||
[maxx, midy + hgap],
|
||||
[maxx, maxy],
|
||||
[midx + hgap, maxy]]
|
||||
cases = {"tb": [[pts[0], pts[1], pts[4], pts[5]],
|
||||
[pts[6], pts[7], pts[10], pts[11]]],
|
||||
"lr": [[pts[9], pts[10], pts[1], pts[2]],
|
||||
[pts[3], pts[4], pts[7], pts[8]]],
|
||||
"4": [[pts[0], pts[1], pts[2]],
|
||||
[pts[3], pts[4], pts[5]],
|
||||
[pts[6], pts[7], pts[8]],
|
||||
[pts[9], pts[10], pts[11]]]}
|
||||
cuts = cases[self.options['gaps']]
|
||||
geo_obj.solid_geometry = cascaded_union([LineString(segment) for segment in cuts])
|
||||
|
||||
# TODO: Check for None
|
||||
self.app.new_object("geometry", name, geo_init)
|
||||
|
||||
def on_iso_button_click(self, *args):
|
||||
self.read_form()
|
||||
dia = self.options["isotooldia"]
|
||||
passes = int(self.options["isopasses"])
|
||||
overlap = self.options["isooverlap"] * dia
|
||||
|
||||
for i in range(passes):
|
||||
|
||||
offset = (2*i + 1)/2.0 * dia - i*overlap
|
||||
iso_name = self.options["name"] + "_iso%d" % (i+1)
|
||||
|
||||
# TODO: This is ugly. Create way to pass data into init function.
|
||||
def iso_init(geo_obj, app_obj):
|
||||
# Propagate options
|
||||
geo_obj.options["cnctooldia"] = self.options["isotooldia"]
|
||||
|
||||
geo_obj.solid_geometry = self.isolation_geometry(offset)
|
||||
app_obj.info("Isolation geometry created: %s" % geo_obj.options["name"])
|
||||
|
||||
# TODO: Do something if this is None. Offer changing name?
|
||||
self.app.new_object("geometry", iso_name, iso_init)
|
||||
|
||||
def on_plot_cb_click(self, *args):
|
||||
if self.muted_ui:
|
||||
return
|
||||
self.read_form_item('plot')
|
||||
self.plot()
|
||||
|
||||
def on_solid_cb_click(self, *args):
|
||||
if self.muted_ui:
|
||||
return
|
||||
self.read_form_item('solid')
|
||||
self.plot()
|
||||
|
||||
def on_multicolored_cb_click(self, *args):
|
||||
if self.muted_ui:
|
||||
return
|
||||
self.read_form_item('multicolored')
|
||||
self.plot()
|
||||
|
||||
def convert_units(self, units):
|
||||
"""
|
||||
Converts the units of the object by scaling dimensions in all geometry
|
||||
|
@ -354,10 +524,20 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
|
|||
|
||||
def __init__(self, name):
|
||||
Excellon.__init__(self)
|
||||
FlatCAMObj.__init__(self, name)
|
||||
FlatCAMObj.__init__(self, name, ExcellonObjectUI())
|
||||
|
||||
self.kind = "excellon"
|
||||
|
||||
self.form_fields.update({
|
||||
"name": self.ui.name_entry,
|
||||
"plot": self.ui.plot_cb,
|
||||
"solid": self.ui.solid_cb,
|
||||
"drillz": self.ui.cutz_entry,
|
||||
"travelz": self.ui.travelz_entry,
|
||||
"feedrate": self.ui.feedrate_entry,
|
||||
"toolselection": self.ui.tools_entry
|
||||
})
|
||||
|
||||
self.options.update({
|
||||
"plot": True,
|
||||
"solid": False,
|
||||
|
@ -367,14 +547,14 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
|
|||
"toolselection": ""
|
||||
})
|
||||
|
||||
self.form_kinds.update({
|
||||
"plot": "cb",
|
||||
"solid": "cb",
|
||||
"drillz": "entry_eval",
|
||||
"travelz": "entry_eval",
|
||||
"feedrate": "entry_eval",
|
||||
"toolselection": "entry_text"
|
||||
})
|
||||
# self.form_kinds.update({
|
||||
# "plot": "cb",
|
||||
# "solid": "cb",
|
||||
# "drillz": "entry_eval",
|
||||
# "travelz": "entry_eval",
|
||||
# "feedrate": "entry_eval",
|
||||
# "toolselection": "entry_text"
|
||||
# })
|
||||
|
||||
# TODO: Document this.
|
||||
self.tool_cbs = {}
|
||||
|
@ -384,6 +564,23 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
|
|||
# from predecessors.
|
||||
self.ser_attrs += ['options', 'kind']
|
||||
|
||||
self.ui.plot_cb.connect('clicked', self.on_plot_cb_click)
|
||||
self.ui.plot_cb.connect('activate', self.on_plot_cb_click)
|
||||
self.ui.solid_cb.connect('clicked', self.on_solid_cb_click)
|
||||
self.ui.solid_cb.connect('activate', self.on_solid_cb_click)
|
||||
|
||||
def on_plot_cb_click(self, *args):
|
||||
if self.muted_ui:
|
||||
return
|
||||
self.read_form_item('plot')
|
||||
self.plot()
|
||||
|
||||
def on_solid_cb_click(self, *args):
|
||||
if self.muted_ui:
|
||||
return
|
||||
self.read_form_item('solid')
|
||||
self.plot()
|
||||
|
||||
def convert_units(self, units):
|
||||
factor = Excellon.convert_units(self, units)
|
||||
|
||||
|
@ -457,7 +654,7 @@ class FlatCAMCNCjob(FlatCAMObj, 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)
|
||||
FlatCAMObj.__init__(self, name)
|
||||
FlatCAMObj.__init__(self, name, CNCObjectUI())
|
||||
|
||||
self.kind = "cncjob"
|
||||
|
||||
|
@ -466,16 +663,31 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
|
|||
"tooldia": 0.4 / 25.4 # 0.4mm in inches
|
||||
})
|
||||
|
||||
self.form_kinds.update({
|
||||
"plot": "cb",
|
||||
"tooldia": "entry_eval"
|
||||
self.form_fields.update({
|
||||
"name": self.ui.name_entry,
|
||||
"plot": self.ui.plot_cb,
|
||||
"tooldia": self.ui.tooldia_entry
|
||||
})
|
||||
|
||||
# self.form_kinds.update({
|
||||
# "plot": "cb",
|
||||
# "tooldia": "entry_eval"
|
||||
# })
|
||||
|
||||
# Attributes to be included in serialization
|
||||
# Always append to it because it carries contents
|
||||
# from predecessors.
|
||||
self.ser_attrs += ['options', 'kind']
|
||||
|
||||
self.ui.plot_cb.connect('clicked', self.on_plot_cb_click)
|
||||
self.ui.plot_cb.connect('activate', self.on_plot_cb_click)
|
||||
|
||||
def on_plot_cb_click(self, *args):
|
||||
if self.muted_ui:
|
||||
return
|
||||
self.read_form_item('plot')
|
||||
self.plot()
|
||||
|
||||
def plot(self):
|
||||
|
||||
# Does all the required setup and returns False
|
||||
|
@ -501,15 +713,29 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
"""
|
||||
|
||||
def __init__(self, name):
|
||||
FlatCAMObj.__init__(self, name)
|
||||
FlatCAMObj.__init__(self, name, GeometryObjectUI())
|
||||
Geometry.__init__(self)
|
||||
|
||||
self.kind = "geometry"
|
||||
|
||||
self.form_fields.update({
|
||||
"name": self.ui.name_entry,
|
||||
"plot": self.ui.plot_cb,
|
||||
# "solid": self.ui.sol,
|
||||
# "multicolored": self.ui.,
|
||||
"cutz": self.ui.cutz_entry,
|
||||
"travelz": self.ui.travelz_entry,
|
||||
"feedrate": self.ui.cncfeedrate_entry,
|
||||
"cnctooldia": self.ui.cnctooldia_entry,
|
||||
"painttooldia": self.ui.painttooldia_entry,
|
||||
"paintoverlap": self.ui.paintoverlap_entry,
|
||||
"paintmargin": self.ui.paintmargin_entry
|
||||
})
|
||||
|
||||
self.options.update({
|
||||
"plot": True,
|
||||
"solid": False,
|
||||
"multicolored": False,
|
||||
# "solid": False,
|
||||
# "multicolored": False,
|
||||
"cutz": -0.002,
|
||||
"travelz": 0.1,
|
||||
"feedrate": 5.0,
|
||||
|
@ -519,24 +745,105 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
"paintmargin": 0.01
|
||||
})
|
||||
|
||||
self.form_kinds.update({
|
||||
"plot": "cb",
|
||||
"solid": "cb",
|
||||
"multicolored": "cb",
|
||||
"cutz": "entry_eval",
|
||||
"travelz": "entry_eval",
|
||||
"feedrate": "entry_eval",
|
||||
"cnctooldia": "entry_eval",
|
||||
"painttooldia": "entry_eval",
|
||||
"paintoverlap": "entry_eval",
|
||||
"paintmargin": "entry_eval"
|
||||
})
|
||||
# self.form_kinds.update({
|
||||
# "plot": "cb",
|
||||
# "solid": "cb",
|
||||
# "multicolored": "cb",
|
||||
# "cutz": "entry_eval",
|
||||
# "travelz": "entry_eval",
|
||||
# "feedrate": "entry_eval",
|
||||
# "cnctooldia": "entry_eval",
|
||||
# "painttooldia": "entry_eval",
|
||||
# "paintoverlap": "entry_eval",
|
||||
# "paintmargin": "entry_eval"
|
||||
# })
|
||||
|
||||
# Attributes to be included in serialization
|
||||
# Always append to it because it carries contents
|
||||
# from predecessors.
|
||||
self.ser_attrs += ['options', 'kind']
|
||||
|
||||
assert isinstance(self.ui, GeometryObjectUI)
|
||||
self.ui.plot_cb.connect('clicked', self.on_plot_cb_click)
|
||||
self.ui.plot_cb.connect('activate', self.on_plot_cb_click)
|
||||
self.ui.generate_cnc_button.connect('clicked', self.on_generatecnc_button_click)
|
||||
self.ui.generate_cnc_button.connect('activate', self.on_generatecnc_button_click)
|
||||
self.ui.generate_paint_button.connect('clicked', self.on_paint_button_click)
|
||||
self.ui.generate_paint_button.connect('activate', self.on_paint_button_click)
|
||||
|
||||
def on_paint_button_click(self, *args):
|
||||
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
|
||||
|
||||
# To be called after clicking on the plot.
|
||||
def doit(event):
|
||||
self.app.plotcanvas.mpl_disconnect(subscription)
|
||||
point = [event.xdata, event.ydata]
|
||||
poly = find_polygon(self.solid_geometry, point)
|
||||
|
||||
# Initializes the new geometry object
|
||||
def gen_paintarea(geo_obj, app_obj):
|
||||
assert isinstance(geo_obj, FlatCAMGeometry)
|
||||
#assert isinstance(app_obj, App)
|
||||
cp = clear_poly(poly.buffer(-self.options["paintmargin"]), tooldia, overlap)
|
||||
geo_obj.solid_geometry = cp
|
||||
geo_obj.options["cnctooldia"] = tooldia
|
||||
|
||||
name = self.options["name"] + "_paint"
|
||||
self.new_object("geometry", name, gen_paintarea)
|
||||
|
||||
subscription = self.app.plotcanvas.mpl_connect('button_press_event', doit)
|
||||
|
||||
def on_generatecnc_button_click(self, *args):
|
||||
self.read_form()
|
||||
job_name = self.options["name"] + "_cnc"
|
||||
|
||||
# Object initialization function for app.new_object()
|
||||
# RUNNING ON SEPARATE THREAD!
|
||||
def job_init(job_obj, app_obj):
|
||||
assert isinstance(job_obj, FlatCAMCNCjob)
|
||||
# Propagate options
|
||||
job_obj.options["tooldia"] = self.options["cnctooldia"]
|
||||
|
||||
GLib.idle_add(lambda: app_obj.set_progress_bar(0.2, "Creating CNC Job..."))
|
||||
job_obj.z_cut = self.options["cutz"]
|
||||
job_obj.z_move = self.options["travelz"]
|
||||
job_obj.feedrate = self.options["feedrate"]
|
||||
|
||||
GLib.idle_add(lambda: app_obj.set_progress_bar(0.4, "Analyzing Geometry..."))
|
||||
# TODO: The tolerance should not be hard coded. Just for testing.
|
||||
job_obj.generate_from_geometry(self, tolerance=0.0005)
|
||||
|
||||
GLib.idle_add(lambda: app_obj.set_progress_bar(0.5, "Parsing G-Code..."))
|
||||
job_obj.gcode_parse()
|
||||
|
||||
# TODO: job_obj.create_geometry creates stuff that is not used.
|
||||
#GLib.idle_add(lambda: app_obj.set_progress_bar(0.6, "Creating New Geometry..."))
|
||||
#job_obj.create_geometry()
|
||||
|
||||
GLib.idle_add(lambda: app_obj.set_progress_bar(0.8, "Plotting..."))
|
||||
|
||||
# To be run in separate thread
|
||||
def job_thread(app_obj):
|
||||
app_obj.new_object("cncjob", job_name, job_init)
|
||||
GLib.idle_add(lambda: app_obj.info("CNCjob created: %s" % job_name))
|
||||
GLib.idle_add(lambda: app_obj.set_progress_bar(1.0, "Done!"))
|
||||
GLib.timeout_add_seconds(1, lambda: app_obj.set_progress_bar(0.0, "Idle"))
|
||||
|
||||
# Send to worker
|
||||
self.app.worker.add_task(job_thread, [self.app])
|
||||
|
||||
def on_plot_cb_click(self, *args):
|
||||
if self.muted_ui:
|
||||
return
|
||||
self.read_form_item('plot')
|
||||
self.plot()
|
||||
|
||||
def scale(self, factor):
|
||||
"""
|
||||
Scales all geometry by a given factor.
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
from gi.repository import Gtk
|
||||
import re
|
||||
from copy import copy
|
||||
|
||||
|
||||
class RadioSet(Gtk.Box):
|
||||
def __init__(self, choices):
|
||||
"""
|
||||
The choices are specified as a list of dictionaries containing:
|
||||
|
||||
* 'label': Shown in the UI
|
||||
* 'value': The value returned is selected
|
||||
|
||||
:param choices: List of choices. See description.
|
||||
:type choices: list
|
||||
"""
|
||||
Gtk.Box.__init__(self)
|
||||
self.choices = copy(choices)
|
||||
self.group = None
|
||||
for choice in self.choices:
|
||||
if self.group is None:
|
||||
choice['radio'] = Gtk.RadioButton.new_with_label(None, choice['label'])
|
||||
self.group = choice['radio']
|
||||
else:
|
||||
choice['radio'] = Gtk.RadioButton.new_with_label_from_widget(self.group, choice['label'])
|
||||
self.pack_start(choice['radio'], expand=True, fill=False, padding=2)
|
||||
# choice['radio'].connect('toggled', self.on_toggle)
|
||||
|
||||
# def on_toggle(self, *args):
|
||||
# return
|
||||
|
||||
def get_value(self):
|
||||
for choice in self.choices:
|
||||
if choice['radio'].get_active():
|
||||
return choice['value']
|
||||
print "ERROR: No button was toggled in RadioSet."
|
||||
return None
|
||||
|
||||
def set_value(self, val):
|
||||
for choice in self.choices:
|
||||
if choice['value'] == val:
|
||||
choice['radio'].set_active(True)
|
||||
return
|
||||
print "ERROR: Value given is not part of this RadioSet:", val
|
||||
|
||||
|
||||
class LengthEntry(Gtk.Entry):
|
||||
def __init__(self, output_units='IN'):
|
||||
Gtk.Entry.__init__(self)
|
||||
self.output_units = output_units
|
||||
self.format_re = re.compile(r"^([^\s]+)(?:\s([a-zA-Z]+))?$")
|
||||
|
||||
# Unit conversion table OUTPUT-INPUT
|
||||
self.scales = {
|
||||
'IN': {'MM': 1/25.4},
|
||||
'MM': {'IN': 25.4}
|
||||
}
|
||||
|
||||
self.connect('activate', self.on_activate)
|
||||
|
||||
def on_activate(self, *args):
|
||||
val = self.get_value()
|
||||
if val is not None:
|
||||
self.set_text(str(val))
|
||||
else:
|
||||
print "WARNING: Could not interpret entry:", self.get_text()
|
||||
|
||||
def get_value(self):
|
||||
raw = self.get_text().strip(' ')
|
||||
match = self.format_re.search(raw)
|
||||
if not match:
|
||||
return None
|
||||
try:
|
||||
if match.group(2) is not None and match.group(2).upper() in self.scales:
|
||||
return float(match.group(1))*self.scales[self.output_units][match.group(2).upper()]
|
||||
else:
|
||||
return float(match.group(1))
|
||||
except:
|
||||
print "ERROR: Could not parse value in entry:", raw
|
||||
return None
|
||||
|
||||
def set_value(self, val):
|
||||
self.set_text(str(val))
|
||||
|
||||
|
||||
class FloatEntry(Gtk.Entry):
|
||||
def __init__(self):
|
||||
Gtk.Entry.__init__(self)
|
||||
|
||||
self.connect('activate', self.on_activate)
|
||||
|
||||
def on_activate(self, *args):
|
||||
val = self.get_value()
|
||||
if val is not None:
|
||||
self.set_text(str(val))
|
||||
else:
|
||||
print "WARNING: Could not interpret entry:", self.get_text()
|
||||
|
||||
def get_value(self):
|
||||
raw = self.get_text().strip(' ')
|
||||
try:
|
||||
evaled = eval(raw)
|
||||
except:
|
||||
print "ERROR: Could not evaluate:", raw
|
||||
return None
|
||||
|
||||
return float(evaled)
|
||||
|
||||
def set_value(self, val):
|
||||
self.set_text(str(val))
|
||||
|
||||
|
||||
class IntEntry(Gtk.Entry):
|
||||
def __init__(self):
|
||||
Gtk.Entry.__init__(self)
|
||||
|
||||
def get_value(self):
|
||||
return int(self.get_text())
|
||||
|
||||
def set_value(self, val):
|
||||
self.set_text(str(val))
|
||||
|
||||
|
||||
class FCEntry(Gtk.Entry):
|
||||
def __init__(self):
|
||||
Gtk.Entry.__init__(self)
|
||||
|
||||
def get_value(self):
|
||||
return self.get_text()
|
||||
|
||||
def set_value(self, val):
|
||||
self.set_text(str(val))
|
||||
|
||||
|
||||
class FCCheckBox(Gtk.CheckButton):
|
||||
def __init__(self, label=''):
|
||||
Gtk.CheckButton.__init__(self, label=label)
|
||||
|
||||
def get_value(self):
|
||||
return self.get_active()
|
||||
|
||||
def set_value(self, val):
|
||||
self.set_active(val)
|
|
@ -0,0 +1,254 @@
|
|||
############################################################
|
||||
# FlatCAM: 2D Post-processing for Manufacturing #
|
||||
# http://caram.cl/software/flatcam #
|
||||
# Author: Juan Pablo Caram (c) #
|
||||
# Date: 4/20/2014 #
|
||||
# MIT Licence #
|
||||
############################################################
|
||||
|
||||
from FlatCAMObj import *
|
||||
from gi.repository import Gtk, GdkPixbuf
|
||||
import inspect # TODO: Remove
|
||||
|
||||
|
||||
class ObjectCollection:
|
||||
|
||||
classdict = {
|
||||
"gerber": FlatCAMGerber,
|
||||
"excellon": FlatCAMExcellon,
|
||||
"cncjob": FlatCAMCNCjob,
|
||||
"geometry": FlatCAMGeometry
|
||||
}
|
||||
|
||||
icon_files = {
|
||||
"gerber": "share/flatcam_icon16.png",
|
||||
"excellon": "share/drill16.png",
|
||||
"cncjob": "share/cnc16.png",
|
||||
"geometry": "share/geometry16.png"
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
|
||||
### Icons for the list view
|
||||
self.icons = {}
|
||||
for kind in ObjectCollection.icon_files:
|
||||
self.icons[kind] = GdkPixbuf.Pixbuf.new_from_file(ObjectCollection.icon_files[kind])
|
||||
|
||||
### GUI List components
|
||||
## Model
|
||||
self.store = Gtk.ListStore(FlatCAMObj)
|
||||
|
||||
## View
|
||||
self.view = Gtk.TreeView(model=self.store)
|
||||
#self.view.connect("row_activated", self.on_row_activated)
|
||||
self.tree_selection = self.view.get_selection()
|
||||
self.change_subscription = self.tree_selection.connect("changed", self.on_list_selection_change)
|
||||
|
||||
## Renderers
|
||||
# Icon
|
||||
renderer_pixbuf = Gtk.CellRendererPixbuf()
|
||||
column_pixbuf = Gtk.TreeViewColumn("Type", renderer_pixbuf)
|
||||
|
||||
def _set_cell_icon(column, cell, model, it, data):
|
||||
obj = model.get_value(it, 0)
|
||||
cell.set_property('pixbuf', self.icons[obj.kind])
|
||||
|
||||
column_pixbuf.set_cell_data_func(renderer_pixbuf, _set_cell_icon)
|
||||
self.view.append_column(column_pixbuf)
|
||||
|
||||
# Name
|
||||
renderer_text = Gtk.CellRendererText()
|
||||
column_text = Gtk.TreeViewColumn("Name", renderer_text)
|
||||
|
||||
def _set_cell_text(column, cell, model, it, data):
|
||||
obj = model.get_value(it, 0)
|
||||
cell.set_property('text', obj.options["name"])
|
||||
|
||||
column_text.set_cell_data_func(renderer_text, _set_cell_text)
|
||||
self.view.append_column(column_text)
|
||||
|
||||
def print_list(self):
|
||||
iterat = self.store.get_iter_first()
|
||||
while iterat is not None:
|
||||
obj = self.store[iterat][0]
|
||||
print obj
|
||||
iterat = self.store.iter_next(iterat)
|
||||
|
||||
def delete_all(self):
|
||||
print "OC.delete_all()"
|
||||
self.store.clear()
|
||||
|
||||
def delete_active(self):
|
||||
print "OC.delete_active()"
|
||||
try:
|
||||
model, treeiter = self.tree_selection.get_selected()
|
||||
self.store.remove(treeiter)
|
||||
except:
|
||||
pass
|
||||
|
||||
def on_list_selection_change(self, selection):
|
||||
"""
|
||||
Callback for change in selection on the objects' list.
|
||||
Instructs the new selection to build the UI for its options.
|
||||
|
||||
:param selection: Ignored.
|
||||
:return: None
|
||||
"""
|
||||
print inspect.stack()[1][3], "--> OC.on_list_selection_change()"
|
||||
try:
|
||||
self.get_active().build_ui()
|
||||
except AttributeError: # For None being active
|
||||
pass
|
||||
|
||||
def set_active(self, name):
|
||||
"""
|
||||
Sets an object as the active object in the program. Same
|
||||
as `set_list_selection()`.
|
||||
|
||||
:param name: Name of the object.
|
||||
:type name: str
|
||||
:return: None
|
||||
"""
|
||||
print "OC.set_active()"
|
||||
self.set_list_selection(name)
|
||||
|
||||
def get_active(self):
|
||||
print inspect.stack()[1][3], "--> OC.get_active()"
|
||||
try:
|
||||
model, treeiter = self.tree_selection.get_selected()
|
||||
return model[treeiter][0]
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
def set_list_selection(self, name):
|
||||
"""
|
||||
Sets which object should be selected in the list.
|
||||
|
||||
:param name: Name of the object.
|
||||
:rtype name: str
|
||||
:return: None
|
||||
"""
|
||||
print inspect.stack()[1][3], "--> OC.set_list_selection()"
|
||||
iterat = self.store.get_iter_first()
|
||||
while iterat is not None and self.store[iterat][0].options["name"] != name:
|
||||
iterat = self.store.iter_next(iterat)
|
||||
self.tree_selection.select_iter(iterat)
|
||||
|
||||
def append(self, obj, active=False):
|
||||
"""
|
||||
Add a FlatCAMObj the the collection. This method is thread-safe.
|
||||
|
||||
:param obj: FlatCAMObj to append
|
||||
:type obj: FlatCAMObj
|
||||
:param active: If it is to become the active object after appending
|
||||
:type active: bool
|
||||
:return: None
|
||||
"""
|
||||
print inspect.stack()[1][3], "--> OC.append()"
|
||||
|
||||
def guitask():
|
||||
self.store.append([obj])
|
||||
if active:
|
||||
self.set_list_selection(obj.options["name"])
|
||||
GLib.idle_add(guitask)
|
||||
|
||||
def get_names(self):
|
||||
"""
|
||||
Gets a list of the names of all objects in the collection.
|
||||
|
||||
:return: List of names.
|
||||
:rtype: list
|
||||
"""
|
||||
print "OC.get_names()"
|
||||
names = []
|
||||
iterat = self.store.get_iter_first()
|
||||
while iterat is not None:
|
||||
obj = self.store[iterat][0]
|
||||
names.append(obj.options["name"])
|
||||
iterat = self.store.iter_next(iterat)
|
||||
return names
|
||||
|
||||
def get_bounds(self):
|
||||
"""
|
||||
Finds coordinates bounding all objects in the collection.
|
||||
|
||||
:return: [xmin, ymin, xmax, ymax]
|
||||
:rtype: list
|
||||
"""
|
||||
print "OC.get_bounds()"
|
||||
|
||||
# TODO: Move the operation out of here.
|
||||
|
||||
xmin = Inf
|
||||
ymin = Inf
|
||||
xmax = -Inf
|
||||
ymax = -Inf
|
||||
|
||||
iterat = self.store.get_iter_first()
|
||||
while iterat is not None:
|
||||
obj = self.store[iterat][0]
|
||||
try:
|
||||
gxmin, gymin, gxmax, gymax = obj.bounds()
|
||||
xmin = min([xmin, gxmin])
|
||||
ymin = min([ymin, gymin])
|
||||
xmax = max([xmax, gxmax])
|
||||
ymax = max([ymax, gymax])
|
||||
except:
|
||||
print "DEV WARNING: Tried to get bounds of empty geometry."
|
||||
iterat = self.store.iter_next(iterat)
|
||||
return [xmin, ymin, xmax, ymax]
|
||||
|
||||
def get_list(self):
|
||||
"""
|
||||
Returns a list with all FlatCAMObj.
|
||||
|
||||
:return: List with all FlatCAMObj.
|
||||
:rtype: list
|
||||
"""
|
||||
collection_list = []
|
||||
iterat = self.store.get_iter_first()
|
||||
while iterat is not None:
|
||||
obj = self.store[iterat][0]
|
||||
collection_list.append(obj)
|
||||
iterat = self.store.iter_next(iterat)
|
||||
return collection_list
|
||||
|
||||
def get_by_name(self, name):
|
||||
"""
|
||||
Fetches the FlatCAMObj with the given `name`.
|
||||
|
||||
:param name: The name of the object.
|
||||
:type name: str
|
||||
:return: The requested object or None if no such object.
|
||||
:rtype: FlatCAMObj or None
|
||||
"""
|
||||
iterat = self.store.get_iter_first()
|
||||
while iterat is not None:
|
||||
obj = self.store[iterat][0]
|
||||
if obj.options["name"] == name:
|
||||
return obj
|
||||
iterat = self.store.iter_next(iterat)
|
||||
return None
|
||||
|
||||
# def change_name(self, old_name, new_name):
|
||||
# """
|
||||
# Changes the name of `FlatCAMObj` named `old_name` to `new_name`.
|
||||
#
|
||||
# :param old_name: Name of the object to change.
|
||||
# :type old_name: str
|
||||
# :param new_name: New name.
|
||||
# :type new_name: str
|
||||
# :return: True if name change succeeded, False otherwise. Will fail
|
||||
# if no object with `old_name` is found.
|
||||
# :rtype: bool
|
||||
# """
|
||||
# print inspect.stack()[1][3], "--> OC.change_name()"
|
||||
# iterat = self.store.get_iter_first()
|
||||
# while iterat is not None:
|
||||
# obj = self.store[iterat][0]
|
||||
# if obj.options["name"] == old_name:
|
||||
# obj.options["name"] = new_name
|
||||
# self.store.row_changed(0, iterat)
|
||||
# return True
|
||||
# iterat = self.store.iter_next(iterat)
|
||||
# return False
|
|
@ -0,0 +1,375 @@
|
|||
|
||||
from gi.repository import Gtk
|
||||
import re
|
||||
from copy import copy
|
||||
|
||||
from GUIElements import *
|
||||
|
||||
|
||||
class ObjectUI(Gtk.VBox):
|
||||
def __init__(self, icon_file='share/flatcam_icon32.png', title='FlatCAM Object'):
|
||||
Gtk.VBox.__init__(self, spacing=3, margin=5, vexpand=False)
|
||||
|
||||
## Page Title box (spacing between children)
|
||||
self.title_box = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 2)
|
||||
self.pack_start(self.title_box, expand=False, fill=False, padding=2)
|
||||
|
||||
## Page Title icon
|
||||
self.icon = Gtk.Image.new_from_file(icon_file)
|
||||
self.title_box.pack_start(self.icon, expand=False, fill=False, padding=2)
|
||||
|
||||
## Title label
|
||||
self.title_label = Gtk.Label()
|
||||
self.title_label.set_markup("<b>" + title + "</b>")
|
||||
self.title_label.set_justify(Gtk.Justification.CENTER)
|
||||
self.title_box.pack_start(self.title_label, expand=False, fill=False, padding=2)
|
||||
|
||||
## Object name
|
||||
self.name_box = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 2)
|
||||
self.pack_start(self.name_box, expand=True, fill=False, padding=2)
|
||||
name_label = Gtk.Label('Name:')
|
||||
name_label.set_justify(Gtk.Justification.RIGHT)
|
||||
self.name_box.pack_start(name_label,
|
||||
expand=False, fill=False, padding=2)
|
||||
self.name_entry = FCEntry()
|
||||
self.name_box.pack_start(self.name_entry, expand=True, fill=False, padding=2)
|
||||
|
||||
## Box box for custom widgets
|
||||
self.custom_box = Gtk.VBox(spacing=3, margin=0, vexpand=False)
|
||||
self.pack_start(self.custom_box, expand=False, fill=False, padding=0)
|
||||
|
||||
## Common to all objects
|
||||
## Scale
|
||||
self.scale_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.scale_label.set_markup('<b>Scale:</b>')
|
||||
self.pack_start(self.scale_label, expand=True, fill=False, padding=2)
|
||||
|
||||
grid5 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.pack_start(grid5, expand=True, fill=False, padding=2)
|
||||
|
||||
# Factor
|
||||
l10 = Gtk.Label('Factor:', xalign=1)
|
||||
grid5.attach(l10, 0, 0, 1, 1)
|
||||
self.scale_entry = FloatEntry()
|
||||
self.scale_entry.set_text("1.0")
|
||||
grid5.attach(self.scale_entry, 1, 0, 1, 1)
|
||||
|
||||
# GO Button
|
||||
self.scale_button = Gtk.Button(label='Scale')
|
||||
self.pack_start(self.scale_button, expand=True, fill=False, padding=2)
|
||||
|
||||
## Offset
|
||||
self.offset_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.offset_label.set_markup('<b>Offset:</b>')
|
||||
self.pack_start(self.offset_label, expand=True, fill=False, padding=2)
|
||||
|
||||
grid6 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.pack_start(grid6, expand=True, fill=False, padding=2)
|
||||
|
||||
# Vector
|
||||
l11 = Gtk.Label('Offset Vector:', xalign=1)
|
||||
grid6.attach(l11, 0, 0, 1, 1)
|
||||
self.offsetvector_entry = FCEntry()
|
||||
self.offsetvector_entry.set_text("(0.0, 0.0)")
|
||||
grid6.attach(self.offsetvector_entry, 1, 0, 1, 1)
|
||||
|
||||
self.offset_button = Gtk.Button(label='Scale')
|
||||
self.pack_start(self.offset_button, expand=True, fill=False, padding=2)
|
||||
|
||||
def set_field(self, name, value):
|
||||
getattr(self, name).set_value(value)
|
||||
|
||||
def get_field(self, name):
|
||||
return getattr(self, name).get_value()
|
||||
|
||||
|
||||
class CNCObjectUI(ObjectUI):
|
||||
def __init__(self):
|
||||
ObjectUI.__init__(self, title='CNC Job Object', icon_file='share/cnc32.png')
|
||||
|
||||
## Plot options
|
||||
self.plot_options_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.plot_options_label.set_markup("<b>Plot Options:</b>")
|
||||
self.pack_start(self.plot_options_label, expand=False, fill=True, padding=2)
|
||||
|
||||
grid0 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.pack_start(grid0, expand=True, fill=False, padding=2)
|
||||
|
||||
# Plot CB
|
||||
self.plot_cb = FCCheckBox(label='Plot')
|
||||
grid0.attach(self.plot_cb, 0, 0, 2, 1)
|
||||
|
||||
# Tool dia for plot
|
||||
l1 = Gtk.Label('Tool dia:', xalign=1)
|
||||
grid0.attach(l1, 0, 1, 1, 1)
|
||||
self.tooldia_entry = LengthEntry()
|
||||
grid0.attach(self.tooldia_entry, 1, 1, 1, 1)
|
||||
|
||||
# Update plot button
|
||||
self.updateplot_button = Gtk.Button(label='Update Plot')
|
||||
self.pack_start(self.updateplot_button, expand=True, fill=False, padding=2)
|
||||
|
||||
## Export G-Code
|
||||
self.export_gcode_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.export_gcode_label.set_markup("<b>Export G-Code:</b>")
|
||||
self.pack_start(self.export_gcode_label, expand=True, fill=False, padding=2)
|
||||
|
||||
# GO Button
|
||||
self.export_gcode_button = Gtk.Button(label='Export G-Code')
|
||||
self.pack_start(self.export_gcode_button, expand=True, fill=False, padding=2)
|
||||
|
||||
|
||||
class GeometryObjectUI(ObjectUI):
|
||||
def __init__(self):
|
||||
ObjectUI.__init__(self, title='Geometry Object', icon_file='share/geometry32.png')
|
||||
|
||||
## Plot options
|
||||
self.plot_options_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.plot_options_label.set_markup("<b>Plot Options:</b>")
|
||||
self.custom_box.pack_start(self.plot_options_label, expand=False, fill=True, padding=2)
|
||||
|
||||
grid0 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid0, expand=True, fill=False, padding=2)
|
||||
|
||||
# Plot CB
|
||||
self.plot_cb = FCCheckBox(label='Plot')
|
||||
grid0.attach(self.plot_cb, 0, 0, 1, 1)
|
||||
|
||||
## Create CNC Job
|
||||
self.cncjob_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.cncjob_label.set_markup('<b>Create CNC Job:</b>')
|
||||
self.custom_box.pack_start(self.cncjob_label, expand=True, fill=False, padding=2)
|
||||
|
||||
grid1 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid1, expand=True, fill=False, padding=2)
|
||||
|
||||
# Cut Z
|
||||
l1 = Gtk.Label('Cut Z:', xalign=1)
|
||||
grid1.attach(l1, 0, 0, 1, 1)
|
||||
self.cutz_entry = LengthEntry()
|
||||
grid1.attach(self.cutz_entry, 1, 0, 1, 1)
|
||||
|
||||
# Travel Z
|
||||
l2 = Gtk.Label('Travel Z:', xalign=1)
|
||||
grid1.attach(l2, 0, 1, 1, 1)
|
||||
self.travelz_entry = LengthEntry()
|
||||
grid1.attach(self.travelz_entry, 1, 1, 1, 1)
|
||||
|
||||
l3 = Gtk.Label('Feed rate:', xalign=1)
|
||||
grid1.attach(l3, 0, 2, 1, 1)
|
||||
self.cncfeedrate_entry = LengthEntry()
|
||||
grid1.attach(self.cncfeedrate_entry, 1, 2, 1, 1)
|
||||
|
||||
l4 = Gtk.Label('Tool dia:', xalign=1)
|
||||
grid1.attach(l4, 0, 3, 1, 1)
|
||||
self.cnctooldia_entry = LengthEntry()
|
||||
grid1.attach(self.cnctooldia_entry, 1, 3, 1, 1)
|
||||
|
||||
self.generate_cnc_button = Gtk.Button(label='Generate')
|
||||
self.custom_box.pack_start(self.generate_cnc_button, expand=True, fill=False, padding=2)
|
||||
|
||||
## Paint Area
|
||||
self.paint_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.paint_label.set_markup('<b>Paint Area:</b>')
|
||||
self.custom_box.pack_start(self.paint_label, expand=True, fill=False, padding=2)
|
||||
|
||||
grid2 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid2, expand=True, fill=False, padding=2)
|
||||
|
||||
# Tool dia
|
||||
l5 = Gtk.Label('Tool dia:', xalign=1)
|
||||
grid2.attach(l5, 0, 0, 1, 1)
|
||||
self.painttooldia_entry = LengthEntry()
|
||||
grid2.attach(self.painttooldia_entry, 1, 0, 1, 1)
|
||||
|
||||
# Overlap
|
||||
l6 = Gtk.Label('Overlap:', xalign=1)
|
||||
grid2.attach(l6, 0, 1, 1, 1)
|
||||
self.paintoverlap_entry = LengthEntry()
|
||||
grid2.attach(self.paintoverlap_entry, 1, 1, 1, 1)
|
||||
|
||||
# Margin
|
||||
l7 = Gtk.Label('Margin:', xalign=1)
|
||||
grid2.attach(l7, 0, 2, 1, 1)
|
||||
self.paintmargin_entry = LengthEntry()
|
||||
grid2.attach(self.paintmargin_entry, 1, 2, 1, 1)
|
||||
|
||||
# GO Button
|
||||
self.generate_paint_button = Gtk.Button(label='Generate')
|
||||
self.custom_box.pack_start(self.generate_paint_button, expand=True, fill=False, padding=2)
|
||||
|
||||
|
||||
class ExcellonObjectUI(ObjectUI):
|
||||
def __init__(self):
|
||||
ObjectUI.__init__(self, title='Excellon Object', icon_file='share/drill32.png')
|
||||
|
||||
## Plot options
|
||||
self.plot_options_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.plot_options_label.set_markup("<b>Plot Options:</b>")
|
||||
self.custom_box.pack_start(self.plot_options_label, expand=False, fill=True, padding=2)
|
||||
|
||||
grid0 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid0, expand=True, fill=False, padding=2)
|
||||
|
||||
self.plot_cb = FCCheckBox(label='Plot')
|
||||
grid0.attach(self.plot_cb, 0, 0, 1, 1)
|
||||
|
||||
self.solid_cb = FCCheckBox(label='Solid')
|
||||
grid0.attach(self.solid_cb, 1, 0, 1, 1)
|
||||
|
||||
## Create CNC Job
|
||||
self.cncjob_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.cncjob_label.set_markup('<b>Create CNC Job</b>')
|
||||
self.custom_box.pack_start(self.cncjob_label, expand=True, fill=False, padding=2)
|
||||
|
||||
grid1 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid1, expand=True, fill=False, padding=2)
|
||||
|
||||
l1 = Gtk.Label('Cut Z:', xalign=1)
|
||||
grid1.attach(l1, 0, 0, 1, 1)
|
||||
self.cutz_entry = LengthEntry()
|
||||
grid1.attach(self.cutz_entry, 1, 0, 1, 1)
|
||||
|
||||
l2 = Gtk.Label('Travel Z:', xalign=1)
|
||||
grid1.attach(l2, 0, 1, 1, 1)
|
||||
self.travelz_entry = LengthEntry()
|
||||
grid1.attach(self.travelz_entry, 1, 1, 1, 1)
|
||||
|
||||
l3 = Gtk.Label('Feed rate:', xalign=1)
|
||||
grid1.attach(l3, 0, 2, 1, 1)
|
||||
self.feedrate_entry = LengthEntry()
|
||||
grid1.attach(self.feedrate_entry, 1, 2, 1, 1)
|
||||
|
||||
l4 = Gtk.Label('Tools:', xalign=1)
|
||||
grid1.attach(l4, 0, 3, 1, 1)
|
||||
boxt = Gtk.Box()
|
||||
grid1.attach(boxt, 1, 3, 1, 1)
|
||||
self.tools_entry = FCEntry()
|
||||
boxt.pack_start(self.tools_entry, expand=True, fill=False, padding=2)
|
||||
self.choose_tools_button = Gtk.Button(label='Choose...')
|
||||
boxt.pack_start(self.choose_tools_button, expand=True, fill=False, padding=2)
|
||||
|
||||
self.generate_cnc_button = Gtk.Button(label='Generate')
|
||||
self.custom_box.pack_start(self.generate_cnc_button, expand=True, fill=False, padding=2)
|
||||
|
||||
|
||||
class GerberObjectUI(ObjectUI):
|
||||
def __init__(self):
|
||||
ObjectUI.__init__(self, title='Gerber Object')
|
||||
|
||||
## Plot options
|
||||
self.plot_options_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.plot_options_label.set_markup("<b>Plot Options:</b>")
|
||||
self.custom_box.pack_start(self.plot_options_label, expand=False, fill=True, padding=2)
|
||||
|
||||
grid0 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid0, expand=True, fill=False, padding=2)
|
||||
|
||||
# Plot CB
|
||||
self.plot_cb = FCCheckBox(label='Plot')
|
||||
grid0.attach(self.plot_cb, 0, 0, 1, 1)
|
||||
|
||||
# Solid CB
|
||||
self.solid_cb = FCCheckBox(label='Solid')
|
||||
grid0.attach(self.solid_cb, 1, 0, 1, 1)
|
||||
|
||||
# Multicolored CB
|
||||
self.multicolored_cb = FCCheckBox(label='Multicolored')
|
||||
grid0.attach(self.multicolored_cb, 2, 0, 1, 1)
|
||||
|
||||
## Isolation Routing
|
||||
self.isolation_routing_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.isolation_routing_label.set_markup("<b>Isolation Routing:</b>")
|
||||
self.custom_box.pack_start(self.isolation_routing_label, expand=True, fill=False, padding=2)
|
||||
|
||||
grid = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid, expand=True, fill=False, padding=2)
|
||||
|
||||
l1 = Gtk.Label('Tool diam:', xalign=1)
|
||||
grid.attach(l1, 0, 0, 1, 1)
|
||||
self.iso_tool_dia_entry = LengthEntry()
|
||||
grid.attach(self.iso_tool_dia_entry, 1, 0, 1, 1)
|
||||
|
||||
l2 = Gtk.Label('Width (# passes):', xalign=1)
|
||||
grid.attach(l2, 0, 1, 1, 1)
|
||||
self.iso_width_entry = IntEntry()
|
||||
grid.attach(self.iso_width_entry, 1, 1, 1, 1)
|
||||
|
||||
l3 = Gtk.Label('Pass overlap:', xalign=1)
|
||||
grid.attach(l3, 0, 2, 1, 1)
|
||||
self.iso_overlap_entry = FloatEntry()
|
||||
grid.attach(self.iso_overlap_entry, 1, 2, 1, 1)
|
||||
|
||||
self.generate_iso_button = Gtk.Button(label='Generate Geometry')
|
||||
self.custom_box.pack_start(self.generate_iso_button, expand=True, fill=False, padding=2)
|
||||
|
||||
## Board cuttout
|
||||
self.isolation_routing_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.isolation_routing_label.set_markup("<b>Board cutout:</b>")
|
||||
self.custom_box.pack_start(self.isolation_routing_label, expand=True, fill=False, padding=2)
|
||||
|
||||
grid2 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid2, expand=True, fill=False, padding=2)
|
||||
|
||||
l4 = Gtk.Label('Tool dia:', xalign=1)
|
||||
grid2.attach(l4, 0, 0, 1, 1)
|
||||
self.cutout_tooldia_entry = LengthEntry()
|
||||
grid2.attach(self.cutout_tooldia_entry, 1, 0, 1, 1)
|
||||
|
||||
l5 = Gtk.Label('Margin:', xalign=1)
|
||||
grid2.attach(l5, 0, 1, 1, 1)
|
||||
self.cutout_margin_entry = LengthEntry()
|
||||
grid2.attach(self.cutout_margin_entry, 1, 1, 1, 1)
|
||||
|
||||
l6 = Gtk.Label('Gap size:', xalign=1)
|
||||
grid2.attach(l6, 0, 2, 1, 1)
|
||||
self.cutout_gap_entry = LengthEntry()
|
||||
grid2.attach(self.cutout_gap_entry, 1, 2, 1, 1)
|
||||
|
||||
l7 = Gtk.Label('Gaps:', xalign=1)
|
||||
grid2.attach(l7, 0, 3, 1, 1)
|
||||
self.gaps_radio = RadioSet([{'label': '2 (T/B)', 'value': 'tb'},
|
||||
{'label': '2 (L/R)', 'value': 'lr'},
|
||||
{'label': '4', 'value': '4'}])
|
||||
grid2.attach(self.gaps_radio, 1, 3, 1, 1)
|
||||
|
||||
self.generate_cutout_button = Gtk.Button(label='Generate Geometry')
|
||||
self.custom_box.pack_start(self.generate_cutout_button, expand=True, fill=False, padding=2)
|
||||
|
||||
## Non-copper regions
|
||||
self.noncopper_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.noncopper_label.set_markup("<b>Non-copper regions:</b>")
|
||||
self.custom_box.pack_start(self.noncopper_label, expand=True, fill=False, padding=2)
|
||||
|
||||
grid3 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid3, expand=True, fill=False, padding=2)
|
||||
|
||||
l8 = Gtk.Label('Boundary margin:', xalign=1)
|
||||
grid3.attach(l8, 0, 0, 1, 1)
|
||||
self.noncopper_margin_entry = LengthEntry()
|
||||
grid3.attach(self.noncopper_margin_entry, 1, 0, 1, 1)
|
||||
|
||||
self.noncopper_rounded_cb = FCCheckBox(label="Rounded corners")
|
||||
grid3.attach(self.noncopper_rounded_cb, 0, 1, 2, 1)
|
||||
|
||||
self.generate_noncopper_button = Gtk.Button(label='Generate Geometry')
|
||||
self.custom_box.pack_start(self.generate_noncopper_button, expand=True, fill=False, padding=2)
|
||||
|
||||
## Bounding box
|
||||
self.boundingbox_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.boundingbox_label.set_markup('<b>Bounding Box:</b>')
|
||||
self.custom_box.pack_start(self.boundingbox_label, expand=True, fill=False, padding=2)
|
||||
|
||||
grid4 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid4, expand=True, fill=False, padding=2)
|
||||
|
||||
l9 = Gtk.Label('Boundary Margin:', xalign=1)
|
||||
grid4.attach(l9, 0, 0, 1, 1)
|
||||
self.bbmargin_entry = LengthEntry()
|
||||
grid4.attach(self.bbmargin_entry, 1, 0, 1, 1)
|
||||
|
||||
self.bbrounded_cb = FCCheckBox(label="Rounded corners")
|
||||
grid4.attach(self.bbrounded_cb, 0, 1, 2, 1)
|
||||
|
||||
self.generate_bb_button = Gtk.Button(label='Generate Geometry')
|
||||
self.custom_box.pack_start(self.generate_bb_button, expand=True, fill=False, padding=2)
|
118
camlib.py
118
camlib.py
|
@ -554,40 +554,6 @@ class Gerber (Geometry):
|
|||
| others | Depend on ``type`` |
|
||||
+-----------+-----------------------------------+
|
||||
|
||||
* ``paths`` (list): A path is described by a line an aperture that follows that
|
||||
line. Each paths[i] is a dictionary:
|
||||
|
||||
+------------+------------------------------------------------+
|
||||
| Key | Value |
|
||||
+============+================================================+
|
||||
| linestring | (Shapely.LineString) The actual path. |
|
||||
+------------+------------------------------------------------+
|
||||
| aperture | (str) The key for an aperture in apertures. |
|
||||
+------------+------------------------------------------------+
|
||||
|
||||
* ``flashes`` (list): Flashes are single-point strokes of an aperture. Each
|
||||
is a dictionary:
|
||||
|
||||
+------------+------------------------------------------------+
|
||||
| Key | Value |
|
||||
+============+================================================+
|
||||
| loc | (Point) Shapely Point indicating location. |
|
||||
+------------+------------------------------------------------+
|
||||
| aperture | (str) The key for an aperture in apertures. |
|
||||
+------------+------------------------------------------------+
|
||||
|
||||
* ``regions`` (list): Are surfaces defined by a polygon (Shapely.Polygon),
|
||||
which have an exterior and zero or more interiors. An aperture is also
|
||||
associated with a region. Each is a dictionary:
|
||||
|
||||
+------------+-----------------------------------------------------+
|
||||
| Key | Value |
|
||||
+============+=====================================================+
|
||||
| polygon | (Shapely.Polygon) The polygon defining the region. |
|
||||
+------------+-----------------------------------------------------+
|
||||
| aperture | (str) The key for an aperture in apertures. |
|
||||
+------------+-----------------------------------------------------+
|
||||
|
||||
* ``aperture_macros`` (dictionary): Are predefined geometrical structures
|
||||
that can be instanciated with different parameters in an aperture
|
||||
definition. See ``apertures`` above. The key is the name of the macro,
|
||||
|
@ -635,23 +601,6 @@ class Gerber (Geometry):
|
|||
# ['size':float], ['width':float],
|
||||
# ['height':float]}, ...}
|
||||
self.apertures = {}
|
||||
|
||||
# Paths [{'linestring':LineString, 'aperture':str}]
|
||||
# self.paths = []
|
||||
|
||||
# Buffered Paths [Polygon]
|
||||
# Paths transformed into Polygons by
|
||||
# offsetting the aperture size/2
|
||||
# self.buffered_paths = []
|
||||
|
||||
# Polygon regions [{'polygon':Polygon, 'aperture':str}]
|
||||
# self.regions = []
|
||||
|
||||
# Flashes [{'loc':[float,float], 'aperture':str}]
|
||||
# self.flashes = []
|
||||
|
||||
# Geometry from flashes
|
||||
# self.flash_geometry = []
|
||||
|
||||
# Aperture Macros
|
||||
self.aperture_macros = {}
|
||||
|
@ -659,9 +608,8 @@ class Gerber (Geometry):
|
|||
# Attributes to be included in serialization
|
||||
# Always append to it because it carries contents
|
||||
# from Geometry.
|
||||
self.ser_attrs += ['int_digits', 'frac_digits', 'apertures', 'paths',
|
||||
'buffered_paths', 'regions', 'flashes',
|
||||
'flash_geometry', 'aperture_macros']
|
||||
self.ser_attrs += ['int_digits', 'frac_digits', 'apertures',
|
||||
'aperture_macros', 'solid_geometry']
|
||||
|
||||
#### Parser patterns ####
|
||||
# FS - Format Specification
|
||||
|
@ -1432,8 +1380,8 @@ class Excellon(Geometry):
|
|||
|
||||
self.drills = []
|
||||
|
||||
# Trailing "T" or leading "L"
|
||||
self.zeros = ""
|
||||
# Trailing "T" or leading "L" (default)
|
||||
self.zeros = "L"
|
||||
|
||||
# Attributes to be included in serialization
|
||||
# Always append to it because it carries contents
|
||||
|
@ -1504,6 +1452,9 @@ class Excellon(Geometry):
|
|||
|
||||
# Various stop/pause commands
|
||||
self.stop_re = re.compile(r'^((G04)|(M09)|(M06)|(M00)|(M30))')
|
||||
|
||||
# Parse coordinates
|
||||
self.leadingzeros_re = re.compile(r'^(0*)(\d*)')
|
||||
|
||||
def parse_file(self, filename):
|
||||
"""
|
||||
|
@ -1538,7 +1489,7 @@ class Excellon(Geometry):
|
|||
for eline in elines:
|
||||
line_num += 1
|
||||
|
||||
### Cleanup
|
||||
### Cleanup lines
|
||||
eline = eline.strip(' \r\n')
|
||||
|
||||
## Header Begin/End ##
|
||||
|
@ -1563,13 +1514,15 @@ class Excellon(Geometry):
|
|||
match = self.coordsnoperiod_re.search(eline)
|
||||
if match:
|
||||
try:
|
||||
x = float(match.group(1))/10000
|
||||
#x = float(match.group(1))/10000
|
||||
x = self.parse_number(match.group(1))
|
||||
current_x = x
|
||||
except TypeError:
|
||||
x = current_x
|
||||
|
||||
try:
|
||||
y = float(match.group(2))/10000
|
||||
#y = float(match.group(2))/10000
|
||||
y = self.parse_number(match.group(2))
|
||||
current_y = y
|
||||
except TypeError:
|
||||
y = current_y
|
||||
|
@ -1581,7 +1534,7 @@ class Excellon(Geometry):
|
|||
self.drills.append({'point': Point((x, y)), 'tool': current_tool})
|
||||
continue
|
||||
|
||||
## Coordinates with period ##
|
||||
## Coordinates with period: Use literally. ##
|
||||
match = self.coordsperiod_re.search(eline)
|
||||
if match:
|
||||
try:
|
||||
|
@ -1630,6 +1583,21 @@ class Excellon(Geometry):
|
|||
|
||||
print "WARNING: Line ignored:", eline
|
||||
|
||||
def parse_number(self, number_str):
|
||||
"""
|
||||
Parses coordinate numbers without period.
|
||||
|
||||
:param number_str: String representing the numerical value.
|
||||
:type number_str: str
|
||||
:return: Floating point representation of the number
|
||||
:rtype: foat
|
||||
"""
|
||||
if self.zeros == "L":
|
||||
match = self.leadingzeros_re.search(number_str)
|
||||
return float(number_str)/(10**(len(match.group(2))-2+len(match.group(1))))
|
||||
else: # Trailing
|
||||
return float(number_str)/10000
|
||||
|
||||
def create_geometry(self):
|
||||
"""
|
||||
Creates circles of the tool diameter at every point
|
||||
|
@ -2443,33 +2411,3 @@ def parse_gerber_number(strnumber, frac_digits):
|
|||
"""
|
||||
return int(strnumber)*(10**(-frac_digits))
|
||||
|
||||
|
||||
def parse_gerber_coords(gstr, int_digits, frac_digits):
|
||||
"""
|
||||
Parse Gerber coordinates
|
||||
|
||||
:param gstr: Line of G-Code containing coordinates.
|
||||
:type gstr: str
|
||||
:param int_digits: Number of digits in integer part of a number.
|
||||
:type int_digits: int
|
||||
:param frac_digits: Number of digits in frac_digits part of a number.
|
||||
:type frac_digits: int
|
||||
:return: [x, y] coordinates.
|
||||
:rtype: list
|
||||
"""
|
||||
global gerbx, gerby
|
||||
xindex = gstr.find("X")
|
||||
yindex = gstr.find("Y")
|
||||
index = gstr.find("D")
|
||||
if xindex == -1:
|
||||
x = gerbx
|
||||
y = int(gstr[(yindex+1):index])*(10**(-frac_digits))
|
||||
elif yindex == -1:
|
||||
y = gerby
|
||||
x = int(gstr[(xindex+1):index])*(10**(-frac_digits))
|
||||
else:
|
||||
x = int(gstr[(xindex+1):yindex])*(10**(-frac_digits))
|
||||
y = int(gstr[(yindex+1):index])*(10**(-frac_digits))
|
||||
gerbx = x
|
||||
gerby = y
|
||||
return [x, y]
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"gerber_noncopperrounded": false, "geometry_paintoverlap": 0.15, "geometry_plot": true, "excellon_feedrate": 5.0, "gerber_plot": true, "gerber_mergepolys": true, "excellon_drillz": -0.1, "geometry_feedrate": 3.0, "units": "IN", "excellon_travelz": 0.1, "gerber_multicolored": false, "gerber_solid": true, "gerber_isopasses": 1, "excellon_plot": true, "gerber_isotooldia": 0.016, "cncjob_tooldia": 0.016, "geometry_travelz": 0.1, "gerber_cutoutmargin": 0.2, "excellon_solid": false, "geometry_paintmargin": 0.01, "geometry_cutz": -0.002, "geometry_cnctooldia": 0.016, "gerber_cutouttooldia": 0.07, "geometry_painttooldia": 0.0625, "gerber_gaps": "4", "gerber_bboxmargin": 0.0, "cncjob_plot": true, "gerber_cutoutgapsize": 0.15, "gerber_isooverlap": 0.15, "gerber_bboxrounded": false, "geometry_multicolored": false, "gerber_noncoppermargin": 0.0, "geometry_solid": false}
|
||||
{"gerber_noncopperrounded": false, "geometry_paintoverlap": 0.15, "geometry_plot": true, "excellon_feedrate": 5.0, "gerber_plot": true, "excellon_drillz": -0.1, "geometry_feedrate": 3.0, "units": "IN", "excellon_travelz": 0.1, "gerber_multicolored": false, "gerber_solid": true, "gerber_isopasses": 1, "excellon_plot": true, "gerber_isotooldia": 0.016, "cncjob_tooldia": 0.016, "geometry_travelz": 0.1, "gerber_cutoutmargin": 0.2, "excellon_solid": false, "geometry_paintmargin": 0.01, "geometry_cutz": -0.002, "geometry_cnctooldia": 0.016, "gerber_cutouttooldia": 0.07, "geometry_painttooldia": 0.0625, "gerber_gaps": "4", "gerber_bboxmargin": 0.0, "cncjob_plot": true, "gerber_cutoutgapsize": 0.15, "gerber_isooverlap": 0.15, "gerber_bboxrounded": false, "geometry_multicolored": false, "gerber_noncoppermargin": 0.0, "geometry_solid": false}
|
|
@ -1434,8 +1434,8 @@ side of the main window.</p>
|
|||
</dd></dl>
|
||||
|
||||
<dl class="method">
|
||||
<dt id="FlatCAM.App.versionCheck">
|
||||
<tt class="descname">versionCheck</tt><big>(</big><em>*args</em><big>)</big><a class="headerlink" href="#FlatCAM.App.versionCheck" title="Permalink to this definition">¶</a></dt>
|
||||
<dt id="FlatCAM.App.version_check">
|
||||
<tt class="descname">version_check</tt><big>(</big><em>*args</em><big>)</big><a class="headerlink" href="#FlatCAM.App.version_check" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Checks for the latest version of the program. Alerts the
|
||||
user if theirs is outdated. This method is meant to be run
|
||||
in a saeparate thread.</p>
|
||||
|
|
|
@ -1021,7 +1021,7 @@
|
|||
<table style="width: 100%" class="indextable genindextable"><tr>
|
||||
<td style="width: 33%" valign="top"><dl>
|
||||
|
||||
<dt><a href="app.html#FlatCAM.App.versionCheck">versionCheck() (FlatCAM.App method)</a>
|
||||
<dt><a href="app.html#FlatCAM.App.version_check">version_check() (FlatCAM.App method)</a>
|
||||
</dt>
|
||||
|
||||
</dl></td>
|
||||
|
|
|
@ -1 +1 @@
|
|||
[{"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\VNA\\KiCad_Bridge2\\KiCad_Bridge2-F_Cu.gtl"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\WindMills - Bottom Copper 2.gbr"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\Example1_copper_bottom_Gndplane_modified.gbr"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\Example1_copper_bottom_Gndplane.gbr"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\CC_LOAD_7000164-00_REV_A_copper_top.gbr"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\bedini 7 coils capacitor discharge.gbr"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\Gerbers\\AVR_Transistor_Tester_copper_bottom.GBL"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\Gerbers\\AVR_Transistor_Tester_copper_top.GTL"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\PlacaReles-F_Cu.gtl"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\maitest.gtl"}]
|
||||
[{"kind": "project", "filename": "C:\\Users\\jpcaram\\Dropbox\\VNA\\KiCad_Bridge2\\Bridge2.fcproj"}, {"kind": "excellon", "filename": "C:\\Users\\jpcaram\\Dropbox\\PhD\\Kenney\\Project Outputs for AnalogPredistortion1\\apd.TXT"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\PhD\\Kenney\\Project Outputs for AnalogPredistortion1\\apd.GTL"}, {"kind": "excellon", "filename": "C:\\Users\\jpcaram\\Dropbox\\VNA\\KiCad_Bridge2\\KiCad_Bridge2.drl"}, {"kind": "excellon", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\LockController_v1.0_pcb-RoundHoles.TXT\\LockController_v1.0_pcb-RoundHoles.TXT"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\VNA\\KiCad_Bridge2\\KiCad_Bridge2-F_Cu.gtl"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\WindMills - Bottom Copper 2.gbr"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\Example1_copper_bottom_Gndplane_modified.gbr"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\Example1_copper_bottom_Gndplane.gbr"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\CC_LOAD_7000164-00_REV_A_copper_top.gbr"}]
|
Loading…
Reference in New Issue