|
@@ -29,609 +29,9 @@ import urllib
|
|
|
import copy
|
|
import copy
|
|
|
import random
|
|
import random
|
|
|
|
|
|
|
|
|
|
+from FlatCAMObj import *
|
|
|
|
|
|
|
|
-########################################
|
|
|
|
|
-## FlatCAMObj ##
|
|
|
|
|
-########################################
|
|
|
|
|
-class FlatCAMObj:
|
|
|
|
|
- """
|
|
|
|
|
- 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.
|
|
|
|
|
- """
|
|
|
|
|
-
|
|
|
|
|
- # Instance of the application to which these are related.
|
|
|
|
|
- # The app should set this value.
|
|
|
|
|
- app = None
|
|
|
|
|
-
|
|
|
|
|
- def __init__(self, name):
|
|
|
|
|
- self.options = {"name": name}
|
|
|
|
|
- self.form_kinds = {"name": "entry_text"} # Kind of form element for each option
|
|
|
|
|
- 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
|
|
|
|
|
-
|
|
|
|
|
- def setup_axes(self, figure):
|
|
|
|
|
- """
|
|
|
|
|
- 1) Creates axes if they don't exist. 2) Clears axes. 3) Attaches
|
|
|
|
|
- them to figure if not part of the figure. 4) Sets transparent
|
|
|
|
|
- background. 5) Sets 1:1 scale aspect ratio.
|
|
|
|
|
-
|
|
|
|
|
- :param figure: A Matplotlib.Figure on which to add/configure axes.
|
|
|
|
|
- :type figure: matplotlib.figure.Figure
|
|
|
|
|
- :return: None
|
|
|
|
|
- :rtype: None
|
|
|
|
|
- """
|
|
|
|
|
-
|
|
|
|
|
- if self.axes is None:
|
|
|
|
|
- print "New axes"
|
|
|
|
|
- self.axes = figure.add_axes([0.05, 0.05, 0.9, 0.9],
|
|
|
|
|
- label=self.options["name"])
|
|
|
|
|
- elif self.axes not in figure.axes:
|
|
|
|
|
- print "Clearing and attaching axes"
|
|
|
|
|
- self.axes.cla()
|
|
|
|
|
- figure.add_axes(self.axes)
|
|
|
|
|
- else:
|
|
|
|
|
- print "Clearing Axes"
|
|
|
|
|
- self.axes.cla()
|
|
|
|
|
-
|
|
|
|
|
- # Remove all decoration. The app's axes will have
|
|
|
|
|
- # the ticks and grid.
|
|
|
|
|
- self.axes.set_frame_on(False) # No frame
|
|
|
|
|
- self.axes.set_xticks([]) # No tick
|
|
|
|
|
- self.axes.set_yticks([]) # No ticks
|
|
|
|
|
- self.axes.patch.set_visible(False) # No background
|
|
|
|
|
- self.axes.set_aspect(1)
|
|
|
|
|
-
|
|
|
|
|
- def to_form(self):
|
|
|
|
|
- """
|
|
|
|
|
- Copies options to the UI form.
|
|
|
|
|
-
|
|
|
|
|
- :return: None
|
|
|
|
|
- """
|
|
|
|
|
- for option in self.options:
|
|
|
|
|
- self.set_form_item(option)
|
|
|
|
|
-
|
|
|
|
|
- def read_form(self):
|
|
|
|
|
- """
|
|
|
|
|
- Reads form into ``self.options``.
|
|
|
|
|
-
|
|
|
|
|
- :return: None
|
|
|
|
|
- :rtype: None
|
|
|
|
|
- """
|
|
|
|
|
- for option in self.options:
|
|
|
|
|
- self.read_form_item(option)
|
|
|
|
|
-
|
|
|
|
|
- def build_ui(self):
|
|
|
|
|
- """
|
|
|
|
|
- Sets up the UI/form for this object.
|
|
|
|
|
-
|
|
|
|
|
- :return: None
|
|
|
|
|
- :rtype: None
|
|
|
|
|
- """
|
|
|
|
|
-
|
|
|
|
|
- # Where the UI for this object is drawn
|
|
|
|
|
- box_selected = self.app.builder.get_object("box_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)
|
|
|
|
|
- self.to_form()
|
|
|
|
|
- sw.show()
|
|
|
|
|
-
|
|
|
|
|
- def set_form_item(self, option):
|
|
|
|
|
- """
|
|
|
|
|
- Copies the specified option to the UI form.
|
|
|
|
|
-
|
|
|
|
|
- :param option: Name of the option (Key in ``self.options``).
|
|
|
|
|
- :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
|
|
|
|
|
-
|
|
|
|
|
- def read_form_item(self, option):
|
|
|
|
|
- """
|
|
|
|
|
- Reads the specified option from the UI form into ``self.options``.
|
|
|
|
|
-
|
|
|
|
|
- :param option: Name of the option.
|
|
|
|
|
- :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
|
|
|
|
|
-
|
|
|
|
|
- def plot(self):
|
|
|
|
|
- """
|
|
|
|
|
- Plot this object (Extend this method to implement the actual plotting).
|
|
|
|
|
- Axes get created, appended to canvas and cleared before plotting.
|
|
|
|
|
- Call this in descendants before doing the plotting.
|
|
|
|
|
-
|
|
|
|
|
- :return: Whether to continue plotting or not depending on the "plot" option.
|
|
|
|
|
- :rtype: bool
|
|
|
|
|
- """
|
|
|
|
|
-
|
|
|
|
|
- # Axes must exist and be attached to canvas.
|
|
|
|
|
- if self.axes is None or self.axes not in self.app.plotcanvas.figure.axes:
|
|
|
|
|
- self.axes = self.app.plotcanvas.new_axes(self.options['name'])
|
|
|
|
|
-
|
|
|
|
|
- if not self.options["plot"]:
|
|
|
|
|
- self.axes.cla()
|
|
|
|
|
- self.app.plotcanvas.auto_adjust_axes()
|
|
|
|
|
- return False
|
|
|
|
|
-
|
|
|
|
|
- # Clear axes or we will plot on top of them.
|
|
|
|
|
- self.axes.cla()
|
|
|
|
|
- # GLib.idle_add(self.axes.cla)
|
|
|
|
|
- return True
|
|
|
|
|
-
|
|
|
|
|
- def serialize(self):
|
|
|
|
|
- """
|
|
|
|
|
- Returns a representation of the object as a dictionary so
|
|
|
|
|
- it can be later exported as JSON. Override this method.
|
|
|
|
|
-
|
|
|
|
|
- :return: Dictionary representing the object
|
|
|
|
|
- :rtype: dict
|
|
|
|
|
- """
|
|
|
|
|
- return
|
|
|
|
|
-
|
|
|
|
|
- def deserialize(self, obj_dict):
|
|
|
|
|
- """
|
|
|
|
|
- Re-builds an object from its serialized version.
|
|
|
|
|
-
|
|
|
|
|
- :param obj_dict: Dictionary representing a FlatCAMObj
|
|
|
|
|
- :type obj_dict: dict
|
|
|
|
|
- :return: None
|
|
|
|
|
- """
|
|
|
|
|
- return
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-class FlatCAMGerber(FlatCAMObj, Gerber):
|
|
|
|
|
- """
|
|
|
|
|
- Represents Gerber code.
|
|
|
|
|
- """
|
|
|
|
|
-
|
|
|
|
|
- def __init__(self, name):
|
|
|
|
|
- Gerber.__init__(self)
|
|
|
|
|
- FlatCAMObj.__init__(self, name)
|
|
|
|
|
-
|
|
|
|
|
- self.kind = "gerber"
|
|
|
|
|
-
|
|
|
|
|
- # The 'name' is already in self.options from FlatCAMObj
|
|
|
|
|
- self.options.update({
|
|
|
|
|
- "plot": True,
|
|
|
|
|
- "mergepolys": True,
|
|
|
|
|
- "multicolored": False,
|
|
|
|
|
- "solid": False,
|
|
|
|
|
- "isotooldia": 0.016,
|
|
|
|
|
- "isopasses": 1,
|
|
|
|
|
- "isooverlap": 0.15,
|
|
|
|
|
- "cutouttooldia": 0.07,
|
|
|
|
|
- "cutoutmargin": 0.2,
|
|
|
|
|
- "cutoutgapsize": 0.15,
|
|
|
|
|
- "gaps": "tb",
|
|
|
|
|
- "noncoppermargin": 0.0,
|
|
|
|
|
- "noncopperrounded": False,
|
|
|
|
|
- "bboxmargin": 0.0,
|
|
|
|
|
- "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']
|
|
|
|
|
-
|
|
|
|
|
- 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
|
|
|
|
|
- self.options['cutoutmargin'] *= factor
|
|
|
|
|
- self.options['cutoutgapsize'] *= factor
|
|
|
|
|
- self.options['noncoppermargin'] *= factor
|
|
|
|
|
- self.options['bboxmargin'] *= factor
|
|
|
|
|
-
|
|
|
|
|
- def plot(self):
|
|
|
|
|
-
|
|
|
|
|
- # Does all the required setup and returns False
|
|
|
|
|
- # if the 'ptint' option is set to False.
|
|
|
|
|
- if not FlatCAMObj.plot(self):
|
|
|
|
|
- return
|
|
|
|
|
-
|
|
|
|
|
- if self.options["mergepolys"]:
|
|
|
|
|
- geometry = self.solid_geometry
|
|
|
|
|
- else:
|
|
|
|
|
- geometry = self.buffered_paths + \
|
|
|
|
|
- [poly['polygon'] for poly in self.regions] + \
|
|
|
|
|
- self.flash_geometry
|
|
|
|
|
-
|
|
|
|
|
- if self.options["multicolored"]:
|
|
|
|
|
- linespec = '-'
|
|
|
|
|
- else:
|
|
|
|
|
- linespec = 'k-'
|
|
|
|
|
-
|
|
|
|
|
- if self.options["solid"]:
|
|
|
|
|
- for poly in geometry:
|
|
|
|
|
- # TODO: Too many things hardcoded.
|
|
|
|
|
- patch = PolygonPatch(poly,
|
|
|
|
|
- facecolor="#BBF268",
|
|
|
|
|
- edgecolor="#006E20",
|
|
|
|
|
- alpha=0.75,
|
|
|
|
|
- zorder=2)
|
|
|
|
|
- self.axes.add_patch(patch)
|
|
|
|
|
- else:
|
|
|
|
|
- for poly in geometry:
|
|
|
|
|
- x, y = poly.exterior.xy
|
|
|
|
|
- self.axes.plot(x, y, linespec)
|
|
|
|
|
- for ints in poly.interiors:
|
|
|
|
|
- x, y = ints.coords.xy
|
|
|
|
|
- self.axes.plot(x, y, linespec)
|
|
|
|
|
-
|
|
|
|
|
- # self.app.plotcanvas.auto_adjust_axes()
|
|
|
|
|
- GLib.idle_add(self.app.plotcanvas.auto_adjust_axes)
|
|
|
|
|
-
|
|
|
|
|
- def serialize(self):
|
|
|
|
|
- return {
|
|
|
|
|
- "options": self.options,
|
|
|
|
|
- "kind": self.kind
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-class FlatCAMExcellon(FlatCAMObj, Excellon):
|
|
|
|
|
- """
|
|
|
|
|
- Represents Excellon/Drill code.
|
|
|
|
|
- """
|
|
|
|
|
-
|
|
|
|
|
- def __init__(self, name):
|
|
|
|
|
- Excellon.__init__(self)
|
|
|
|
|
- FlatCAMObj.__init__(self, name)
|
|
|
|
|
-
|
|
|
|
|
- self.kind = "excellon"
|
|
|
|
|
-
|
|
|
|
|
- self.options.update({
|
|
|
|
|
- "plot": True,
|
|
|
|
|
- "solid": False,
|
|
|
|
|
- "drillz": -0.1,
|
|
|
|
|
- "travelz": 0.1,
|
|
|
|
|
- "feedrate": 5.0,
|
|
|
|
|
- "toolselection": ""
|
|
|
|
|
- })
|
|
|
|
|
-
|
|
|
|
|
- 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 = {}
|
|
|
|
|
-
|
|
|
|
|
- # Attributes to be included in serialization
|
|
|
|
|
- # Always append to it because it carries contents
|
|
|
|
|
- # from predecessors.
|
|
|
|
|
- self.ser_attrs += ['options', 'kind']
|
|
|
|
|
-
|
|
|
|
|
- def convert_units(self, units):
|
|
|
|
|
- factor = Excellon.convert_units(self, units)
|
|
|
|
|
-
|
|
|
|
|
- self.options['drillz'] *= factor
|
|
|
|
|
- self.options['travelz'] *= factor
|
|
|
|
|
- self.options['feedrate'] *= factor
|
|
|
|
|
-
|
|
|
|
|
- def plot(self):
|
|
|
|
|
-
|
|
|
|
|
- # Does all the required setup and returns False
|
|
|
|
|
- # if the 'ptint' option is set to False.
|
|
|
|
|
- if not FlatCAMObj.plot(self):
|
|
|
|
|
- return
|
|
|
|
|
-
|
|
|
|
|
- try:
|
|
|
|
|
- _ = iter(self.solid_geometry)
|
|
|
|
|
- except TypeError:
|
|
|
|
|
- self.solid_geometry = [self.solid_geometry]
|
|
|
|
|
-
|
|
|
|
|
- # Plot excellon (All polygons?)
|
|
|
|
|
- if self.options["solid"]:
|
|
|
|
|
- for geo in self.solid_geometry:
|
|
|
|
|
- patch = PolygonPatch(geo,
|
|
|
|
|
- facecolor="#C40000",
|
|
|
|
|
- edgecolor="#750000",
|
|
|
|
|
- alpha=0.75,
|
|
|
|
|
- zorder=3)
|
|
|
|
|
- self.axes.add_patch(patch)
|
|
|
|
|
- else:
|
|
|
|
|
- for geo in self.solid_geometry:
|
|
|
|
|
- x, y = geo.exterior.coords.xy
|
|
|
|
|
- self.axes.plot(x, y, 'r-')
|
|
|
|
|
- for ints in geo.interiors:
|
|
|
|
|
- x, y = ints.coords.xy
|
|
|
|
|
- self.axes.plot(x, y, 'g-')
|
|
|
|
|
-
|
|
|
|
|
- #self.app.plotcanvas.auto_adjust_axes()
|
|
|
|
|
- GLib.idle_add(self.app.plotcanvas.auto_adjust_axes)
|
|
|
|
|
-
|
|
|
|
|
- def show_tool_chooser(self):
|
|
|
|
|
- win = Gtk.Window()
|
|
|
|
|
- box = Gtk.Box(spacing=2)
|
|
|
|
|
- box.set_orientation(Gtk.Orientation(1))
|
|
|
|
|
- win.add(box)
|
|
|
|
|
- for tool in self.tools:
|
|
|
|
|
- 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)
|
|
|
|
|
- win.show_all()
|
|
|
|
|
-
|
|
|
|
|
- def on_accept(widget):
|
|
|
|
|
- win.destroy()
|
|
|
|
|
- tool_list = []
|
|
|
|
|
- for toolx in self.tool_cbs:
|
|
|
|
|
- if self.tool_cbs[toolx].get_active():
|
|
|
|
|
- tool_list.append(toolx)
|
|
|
|
|
- self.options["toolselection"] = ", ".join(tool_list)
|
|
|
|
|
- self.to_form()
|
|
|
|
|
-
|
|
|
|
|
- button.connect("activate", on_accept)
|
|
|
|
|
- button.connect("clicked", on_accept)
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-class FlatCAMCNCjob(FlatCAMObj, CNCjob):
|
|
|
|
|
- """
|
|
|
|
|
- Represents G-Code.
|
|
|
|
|
- """
|
|
|
|
|
-
|
|
|
|
|
- def __init__(self, name, units="in", kind="generic", z_move=0.1,
|
|
|
|
|
- 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)
|
|
|
|
|
-
|
|
|
|
|
- self.kind = "cncjob"
|
|
|
|
|
-
|
|
|
|
|
- self.options.update({
|
|
|
|
|
- "plot": True,
|
|
|
|
|
- "tooldia": 0.4 / 25.4 # 0.4mm in inches
|
|
|
|
|
- })
|
|
|
|
|
-
|
|
|
|
|
- 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']
|
|
|
|
|
-
|
|
|
|
|
- def plot(self):
|
|
|
|
|
-
|
|
|
|
|
- # Does all the required setup and returns False
|
|
|
|
|
- # if the 'ptint' option is set to False.
|
|
|
|
|
- if not FlatCAMObj.plot(self):
|
|
|
|
|
- return
|
|
|
|
|
-
|
|
|
|
|
- self.plot2(self.axes, tooldia=self.options["tooldia"])
|
|
|
|
|
-
|
|
|
|
|
- #self.app.plotcanvas.auto_adjust_axes()
|
|
|
|
|
- GLib.idle_add(self.app.plotcanvas.auto_adjust_axes)
|
|
|
|
|
-
|
|
|
|
|
- def convert_units(self, units):
|
|
|
|
|
- factor = CNCjob.convert_units(self, units)
|
|
|
|
|
- print "FlatCAMCNCjob.convert_units()"
|
|
|
|
|
- self.options["tooldia"] *= factor
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|
|
|
|
- """
|
|
|
|
|
- Geometric object not associated with a specific
|
|
|
|
|
- format.
|
|
|
|
|
- """
|
|
|
|
|
-
|
|
|
|
|
- def __init__(self, name):
|
|
|
|
|
- FlatCAMObj.__init__(self, name)
|
|
|
|
|
- Geometry.__init__(self)
|
|
|
|
|
-
|
|
|
|
|
- self.kind = "geometry"
|
|
|
|
|
-
|
|
|
|
|
- self.options.update({
|
|
|
|
|
- "plot": True,
|
|
|
|
|
- "solid": False,
|
|
|
|
|
- "multicolored": False,
|
|
|
|
|
- "cutz": -0.002,
|
|
|
|
|
- "travelz": 0.1,
|
|
|
|
|
- "feedrate": 5.0,
|
|
|
|
|
- "cnctooldia": 0.4 / 25.4,
|
|
|
|
|
- "painttooldia": 0.0625,
|
|
|
|
|
- "paintoverlap": 0.15,
|
|
|
|
|
- "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"
|
|
|
|
|
- })
|
|
|
|
|
-
|
|
|
|
|
- # Attributes to be included in serialization
|
|
|
|
|
- # Always append to it because it carries contents
|
|
|
|
|
- # from predecessors.
|
|
|
|
|
- self.ser_attrs += ['options', 'kind']
|
|
|
|
|
-
|
|
|
|
|
- def scale(self, factor):
|
|
|
|
|
- """
|
|
|
|
|
- Scales all geometry by a given factor.
|
|
|
|
|
-
|
|
|
|
|
- :param factor: Factor by which to scale the object's geometry/
|
|
|
|
|
- :type factor: float
|
|
|
|
|
- :return: None
|
|
|
|
|
- :rtype: None
|
|
|
|
|
- """
|
|
|
|
|
-
|
|
|
|
|
- if type(self.solid_geometry) == list:
|
|
|
|
|
- self.solid_geometry = [affinity.scale(g, factor, factor, origin=(0, 0))
|
|
|
|
|
- for g in self.solid_geometry]
|
|
|
|
|
- else:
|
|
|
|
|
- self.solid_geometry = affinity.scale(self.solid_geometry, factor, factor,
|
|
|
|
|
- origin=(0, 0))
|
|
|
|
|
-
|
|
|
|
|
- def offset(self, vect):
|
|
|
|
|
- """
|
|
|
|
|
- Offsets all geometry by a given vector/
|
|
|
|
|
-
|
|
|
|
|
- :param vect: (x, y) vector by which to offset the object's geometry.
|
|
|
|
|
- :type vect: tuple
|
|
|
|
|
- :return: None
|
|
|
|
|
- :rtype: None
|
|
|
|
|
- """
|
|
|
|
|
-
|
|
|
|
|
- dx, dy = vect
|
|
|
|
|
-
|
|
|
|
|
- if type(self.solid_geometry) == list:
|
|
|
|
|
- self.solid_geometry = [affinity.translate(g, xoff=dx, yoff=dy)
|
|
|
|
|
- for g in self.solid_geometry]
|
|
|
|
|
- else:
|
|
|
|
|
- self.solid_geometry = affinity.translate(self.solid_geometry, xoff=dx, yoff=dy)
|
|
|
|
|
-
|
|
|
|
|
- def convert_units(self, units):
|
|
|
|
|
- factor = Geometry.convert_units(self, units)
|
|
|
|
|
-
|
|
|
|
|
- self.options['cutz'] *= factor
|
|
|
|
|
- self.options['travelz'] *= factor
|
|
|
|
|
- self.options['feedrate'] *= factor
|
|
|
|
|
- self.options['cnctooldia'] *= factor
|
|
|
|
|
- self.options['painttooldia'] *= factor
|
|
|
|
|
- self.options['paintmargin'] *= factor
|
|
|
|
|
-
|
|
|
|
|
- return factor
|
|
|
|
|
-
|
|
|
|
|
- def plot(self):
|
|
|
|
|
- """
|
|
|
|
|
- Plots the object into its axes. If None, of if the axes
|
|
|
|
|
- are not part of the app's figure, it fetches new ones.
|
|
|
|
|
-
|
|
|
|
|
- :return: None
|
|
|
|
|
- """
|
|
|
|
|
-
|
|
|
|
|
- # Does all the required setup and returns False
|
|
|
|
|
- # if the 'ptint' option is set to False.
|
|
|
|
|
- if not FlatCAMObj.plot(self):
|
|
|
|
|
- return
|
|
|
|
|
-
|
|
|
|
|
- # Make sure solid_geometry is iterable.
|
|
|
|
|
- try:
|
|
|
|
|
- _ = iter(self.solid_geometry)
|
|
|
|
|
- except TypeError:
|
|
|
|
|
- self.solid_geometry = [self.solid_geometry]
|
|
|
|
|
-
|
|
|
|
|
- for geo in self.solid_geometry:
|
|
|
|
|
-
|
|
|
|
|
- if type(geo) == Polygon:
|
|
|
|
|
- x, y = geo.exterior.coords.xy
|
|
|
|
|
- self.axes.plot(x, y, 'r-')
|
|
|
|
|
- for ints in geo.interiors:
|
|
|
|
|
- x, y = ints.coords.xy
|
|
|
|
|
- self.axes.plot(x, y, 'r-')
|
|
|
|
|
- continue
|
|
|
|
|
-
|
|
|
|
|
- if type(geo) == LineString or type(geo) == LinearRing:
|
|
|
|
|
- x, y = geo.coords.xy
|
|
|
|
|
- self.axes.plot(x, y, 'r-')
|
|
|
|
|
- continue
|
|
|
|
|
-
|
|
|
|
|
- if type(geo) == MultiPolygon:
|
|
|
|
|
- for poly in geo:
|
|
|
|
|
- x, y = poly.exterior.coords.xy
|
|
|
|
|
- self.axes.plot(x, y, 'r-')
|
|
|
|
|
- for ints in poly.interiors:
|
|
|
|
|
- x, y = ints.coords.xy
|
|
|
|
|
- self.axes.plot(x, y, 'r-')
|
|
|
|
|
- continue
|
|
|
|
|
-
|
|
|
|
|
- print "WARNING: Did not plot:", str(type(geo))
|
|
|
|
|
-
|
|
|
|
|
- #self.app.plotcanvas.auto_adjust_axes()
|
|
|
|
|
- GLib.idle_add(self.app.plotcanvas.auto_adjust_axes)
|
|
|
|
|
-
|
|
|
|
|
|
|
+from FlatCAMWorker import Worker
|
|
|
|
|
|
|
|
########################################
|
|
########################################
|
|
|
## App ##
|
|
## App ##
|
|
@@ -655,9 +55,12 @@ class App:
|
|
|
# GLib.log_set_handler()
|
|
# GLib.log_set_handler()
|
|
|
|
|
|
|
|
#### GUI ####
|
|
#### GUI ####
|
|
|
|
|
+ # Glade init
|
|
|
self.gladefile = "FlatCAM.ui"
|
|
self.gladefile = "FlatCAM.ui"
|
|
|
self.builder = Gtk.Builder()
|
|
self.builder = Gtk.Builder()
|
|
|
self.builder.add_from_file(self.gladefile)
|
|
self.builder.add_from_file(self.gladefile)
|
|
|
|
|
+
|
|
|
|
|
+ # References to UI widgets
|
|
|
self.window = self.builder.get_object("window1")
|
|
self.window = self.builder.get_object("window1")
|
|
|
self.position_label = self.builder.get_object("label3")
|
|
self.position_label = self.builder.get_object("label3")
|
|
|
self.grid = self.builder.get_object("grid1")
|
|
self.grid = self.builder.get_object("grid1")
|
|
@@ -678,6 +81,7 @@ class App:
|
|
|
self.setup_project_list() # The "Project" tab
|
|
self.setup_project_list() # The "Project" tab
|
|
|
self.setup_component_editor() # The "Selected" tab
|
|
self.setup_component_editor() # The "Selected" tab
|
|
|
|
|
|
|
|
|
|
+ ## Setup the toolbar. Adds buttons.
|
|
|
self.setup_toolbar()
|
|
self.setup_toolbar()
|
|
|
|
|
|
|
|
#### Event handling ####
|
|
#### Event handling ####
|
|
@@ -759,11 +163,16 @@ class App:
|
|
|
self.units_label.set_text("[" + self.options["units"] + "]")
|
|
self.units_label.set_text("[" + self.options["units"] + "]")
|
|
|
self.setup_recent_items()
|
|
self.setup_recent_items()
|
|
|
|
|
|
|
|
|
|
+ self.worker = Worker()
|
|
|
|
|
+ self.worker.daemon = True
|
|
|
|
|
+ self.worker.start()
|
|
|
|
|
+
|
|
|
#### Check for updates ####
|
|
#### Check for updates ####
|
|
|
- self.version = 3
|
|
|
|
|
|
|
+ self.version = 4
|
|
|
t1 = threading.Thread(target=self.versionCheck)
|
|
t1 = threading.Thread(target=self.versionCheck)
|
|
|
t1.daemon = True
|
|
t1.daemon = True
|
|
|
t1.start()
|
|
t1.start()
|
|
|
|
|
+ # self.worker.add_task(self.versionCheck)
|
|
|
|
|
|
|
|
#### For debugging only ###
|
|
#### For debugging only ###
|
|
|
def somethreadfunc(app_obj):
|
|
def somethreadfunc(app_obj):
|
|
@@ -780,7 +189,7 @@ class App:
|
|
|
self.icon48 = GdkPixbuf.Pixbuf.new_from_file('share/flatcam_icon48.png')
|
|
self.icon48 = GdkPixbuf.Pixbuf.new_from_file('share/flatcam_icon48.png')
|
|
|
self.icon16 = GdkPixbuf.Pixbuf.new_from_file('share/flatcam_icon16.png')
|
|
self.icon16 = GdkPixbuf.Pixbuf.new_from_file('share/flatcam_icon16.png')
|
|
|
Gtk.Window.set_default_icon_list([self.icon16, self.icon48, self.icon256])
|
|
Gtk.Window.set_default_icon_list([self.icon16, self.icon48, self.icon256])
|
|
|
- self.window.set_title("FlatCAM - Alpha 3 UNSTABLE - Check for updates!")
|
|
|
|
|
|
|
+ self.window.set_title("FlatCAM - Alpha 4 UNSTABLE")
|
|
|
self.window.set_default_size(900, 600)
|
|
self.window.set_default_size(900, 600)
|
|
|
self.window.show_all()
|
|
self.window.show_all()
|
|
|
|
|
|
|
@@ -900,11 +309,16 @@ class App:
|
|
|
|
|
|
|
|
# Closure needed to create callbacks in a loop.
|
|
# Closure needed to create callbacks in a loop.
|
|
|
# Otherwise late binding occurs.
|
|
# Otherwise late binding occurs.
|
|
|
|
|
+ # def make_callback(func, fname):
|
|
|
|
|
+ # def opener(*args):
|
|
|
|
|
+ # t = threading.Thread(target=lambda: func(fname))
|
|
|
|
|
+ # t.daemon = True
|
|
|
|
|
+ # t.start()
|
|
|
|
|
+ # return opener
|
|
|
|
|
+
|
|
|
def make_callback(func, fname):
|
|
def make_callback(func, fname):
|
|
|
def opener(*args):
|
|
def opener(*args):
|
|
|
- t = threading.Thread(target=lambda: func(fname))
|
|
|
|
|
- t.daemon = True
|
|
|
|
|
- t.start()
|
|
|
|
|
|
|
+ self.worker.add_task(func, [fname])
|
|
|
return opener
|
|
return opener
|
|
|
|
|
|
|
|
try:
|
|
try:
|
|
@@ -1003,9 +417,10 @@ class App:
|
|
|
GLib.idle_add(lambda: self.on_zoom_fit(None))
|
|
GLib.idle_add(lambda: self.on_zoom_fit(None))
|
|
|
GLib.timeout_add(300, lambda: app_obj.set_progress_bar(0.0, ""))
|
|
GLib.timeout_add(300, lambda: app_obj.set_progress_bar(0.0, ""))
|
|
|
|
|
|
|
|
- t = threading.Thread(target=thread_func, args=(self,))
|
|
|
|
|
- t.daemon = True
|
|
|
|
|
- t.start()
|
|
|
|
|
|
|
+ # t = threading.Thread(target=thread_func, args=(self,))
|
|
|
|
|
+ # t.daemon = True
|
|
|
|
|
+ # t.start()
|
|
|
|
|
+ self.worker.add_task(thread_func, [self])
|
|
|
|
|
|
|
|
def get_eval(self, widget_name):
|
|
def get_eval(self, widget_name):
|
|
|
"""
|
|
"""
|
|
@@ -1470,9 +885,10 @@ class App:
|
|
|
GLib.idle_add(app_obj.plotcanvas.auto_adjust_axes)
|
|
GLib.idle_add(app_obj.plotcanvas.auto_adjust_axes)
|
|
|
GLib.timeout_add(300, lambda: app_obj.set_progress_bar(0.0, ""))
|
|
GLib.timeout_add(300, lambda: app_obj.set_progress_bar(0.0, ""))
|
|
|
|
|
|
|
|
- t = threading.Thread(target=thread_func, args=(self,))
|
|
|
|
|
- t.daemon = True
|
|
|
|
|
- t.start()
|
|
|
|
|
|
|
+ # t = threading.Thread(target=thread_func, args=(self,))
|
|
|
|
|
+ # t.daemon = True
|
|
|
|
|
+ # t.start()
|
|
|
|
|
+ self.worker.add_task(thread_func, [self])
|
|
|
|
|
|
|
|
def enable_all_plots(self, *args):
|
|
def enable_all_plots(self, *args):
|
|
|
self.plotcanvas.clear()
|
|
self.plotcanvas.clear()
|
|
@@ -1494,9 +910,10 @@ class App:
|
|
|
GLib.idle_add(app_obj.plotcanvas.auto_adjust_axes)
|
|
GLib.idle_add(app_obj.plotcanvas.auto_adjust_axes)
|
|
|
GLib.timeout_add(300, lambda: app_obj.set_progress_bar(0.0, ""))
|
|
GLib.timeout_add(300, lambda: app_obj.set_progress_bar(0.0, ""))
|
|
|
|
|
|
|
|
- t = threading.Thread(target=thread_func, args=(self,))
|
|
|
|
|
- t.daemon = True
|
|
|
|
|
- t.start()
|
|
|
|
|
|
|
+ # t = threading.Thread(target=thread_func, args=(self,))
|
|
|
|
|
+ # t.daemon = True
|
|
|
|
|
+ # t.start()
|
|
|
|
|
+ self.worker.add_task(thread_func, [self])
|
|
|
|
|
|
|
|
def register_recent(self, kind, filename):
|
|
def register_recent(self, kind, filename):
|
|
|
record = {'kind': kind, 'filename': filename}
|
|
record = {'kind': kind, 'filename': filename}
|
|
@@ -2158,9 +1575,10 @@ class App:
|
|
|
obj.plot()
|
|
obj.plot()
|
|
|
GLib.timeout_add(300, lambda: app_obj.set_progress_bar(0.0, "Idle"))
|
|
GLib.timeout_add(300, lambda: app_obj.set_progress_bar(0.0, "Idle"))
|
|
|
|
|
|
|
|
- t = threading.Thread(target=thread_func, args=(self,))
|
|
|
|
|
- t.daemon = True
|
|
|
|
|
- t.start()
|
|
|
|
|
|
|
+ # t = threading.Thread(target=thread_func, args=(self,))
|
|
|
|
|
+ # t.daemon = True
|
|
|
|
|
+ # t.start()
|
|
|
|
|
+ self.worker.add_task(thread_func, [self])
|
|
|
|
|
|
|
|
def on_generate_excellon_cncjob(self, widget):
|
|
def on_generate_excellon_cncjob(self, widget):
|
|
|
"""
|
|
"""
|
|
@@ -2205,9 +1623,10 @@ class App:
|
|
|
GLib.timeout_add_seconds(1, lambda: app_obj.set_progress_bar(0.0, ""))
|
|
GLib.timeout_add_seconds(1, lambda: app_obj.set_progress_bar(0.0, ""))
|
|
|
|
|
|
|
|
# Start the thread
|
|
# Start the thread
|
|
|
- t = threading.Thread(target=job_thread, args=(self,))
|
|
|
|
|
- t.daemon = True
|
|
|
|
|
- t.start()
|
|
|
|
|
|
|
+ # t = threading.Thread(target=job_thread, args=(self,))
|
|
|
|
|
+ # t.daemon = True
|
|
|
|
|
+ # t.start()
|
|
|
|
|
+ self.worker.add_task(job_thread, [self])
|
|
|
|
|
|
|
|
def on_excellon_tool_choose(self, widget):
|
|
def on_excellon_tool_choose(self, widget):
|
|
|
"""
|
|
"""
|
|
@@ -2397,9 +1816,10 @@ class App:
|
|
|
GLib.timeout_add_seconds(1, lambda: app_obj.set_progress_bar(0.0, ""))
|
|
GLib.timeout_add_seconds(1, lambda: app_obj.set_progress_bar(0.0, ""))
|
|
|
|
|
|
|
|
# Start the thread
|
|
# Start the thread
|
|
|
- t = threading.Thread(target=job_thread, args=(self,))
|
|
|
|
|
- t.daemon = True
|
|
|
|
|
- t.start()
|
|
|
|
|
|
|
+ # t = threading.Thread(target=job_thread, args=(self,))
|
|
|
|
|
+ # t.daemon = True
|
|
|
|
|
+ # t.start()
|
|
|
|
|
+ self.worker.add_task(job_thread, [self])
|
|
|
|
|
|
|
|
def on_generate_paintarea(self, widget):
|
|
def on_generate_paintarea(self, widget):
|
|
|
"""
|
|
"""
|
|
@@ -2623,9 +2043,10 @@ class App:
|
|
|
if response == Gtk.ResponseType.OK:
|
|
if response == Gtk.ResponseType.OK:
|
|
|
filename = dialog.get_filename()
|
|
filename = dialog.get_filename()
|
|
|
dialog.destroy()
|
|
dialog.destroy()
|
|
|
- t = threading.Thread(target=on_success, args=(self, filename))
|
|
|
|
|
- t.daemon = True
|
|
|
|
|
- t.start()
|
|
|
|
|
|
|
+ # t = threading.Thread(target=on_success, args=(self, filename))
|
|
|
|
|
+ # t.daemon = True
|
|
|
|
|
+ # t.start()
|
|
|
|
|
+ self.worker.add_task(on_success, [self, filename])
|
|
|
#on_success(self, filename)
|
|
#on_success(self, filename)
|
|
|
elif response == Gtk.ResponseType.CANCEL:
|
|
elif response == Gtk.ResponseType.CANCEL:
|
|
|
self.info("Open cancelled.") # print("Cancel clicked")
|
|
self.info("Open cancelled.") # print("Cancel clicked")
|
|
@@ -3372,7 +2793,6 @@ class PlotCanvas:
|
|
|
self.pan(0, -0.3)
|
|
self.pan(0, -0.3)
|
|
|
return
|
|
return
|
|
|
|
|
|
|
|
-
|
|
|
|
|
def on_mouse_move(self, event):
|
|
def on_mouse_move(self, event):
|
|
|
"""
|
|
"""
|
|
|
Mouse movement event hadler.
|
|
Mouse movement event hadler.
|
|
@@ -3382,5 +2802,24 @@ class PlotCanvas:
|
|
|
"""
|
|
"""
|
|
|
self.mouse = [event.xdata, event.ydata]
|
|
self.mouse = [event.xdata, event.ydata]
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
+class ObjectCollection:
|
|
|
|
|
+ def __init__(self):
|
|
|
|
|
+ self.collection = []
|
|
|
|
|
+ self.active = None
|
|
|
|
|
+
|
|
|
|
|
+ def set_active(self, name):
|
|
|
|
|
+ for obj in self.collection:
|
|
|
|
|
+ if obj.options['name'] == name:
|
|
|
|
|
+ self.active = obj
|
|
|
|
|
+ return self.active
|
|
|
|
|
+ return None
|
|
|
|
|
+
|
|
|
|
|
+ def get_active(self):
|
|
|
|
|
+ return self.active
|
|
|
|
|
+
|
|
|
|
|
+ def append(self, obj):
|
|
|
|
|
+ self.collection.append(obj)
|
|
|
|
|
+
|
|
|
app = App()
|
|
app = App()
|
|
|
Gtk.main()
|
|
Gtk.main()
|