Warning before overwriting. More flexible Excellon parser (tool numbers). Other small fixes.

This commit is contained in:
Juan Pablo Caram
2014-04-05 00:36:23 -04:00
parent 9740739f05
commit 6c13b7dc59
16 changed files with 519 additions and 315 deletions

View File

@@ -36,6 +36,7 @@ from camlib import *
from FlatCAMObj import *
from FlatCAMWorker import Worker
########################################
## App ##
########################################
@@ -149,13 +150,9 @@ class App:
# self.radios_inv.update({obj.kind + "_" + option: obj.radios_inv[option]})
## Event subscriptions ##
# TODO: Move to plotcanvas
self.plot_click_subscribers = {}
self.plot_mousemove_subscribers = {}
## Tools ##
self.measure = Measurement(self.builder.get_object("box39"), self.plotcanvas.axes,
self.plot_click_subscribers, self.plot_mousemove_subscribers)
self.measure = Measurement(self.builder.get_object("box39"), self.plotcanvas)
# Toolbar icon
# TODO: Where should I put this? Tool should have a method to add to toolbar?
meas_ico = Gtk.Image.new_from_file('share/measure32.png')
@@ -188,10 +185,13 @@ class App:
def somethreadfunc(app_obj):
print "Hello World!"
self.message_dialog("Starting", "The best program is starting")
t = threading.Thread(target=somethreadfunc, args=(self,))
t.daemon = True
t.start()
########################################
## START ##
########################################
@@ -203,6 +203,30 @@ class App:
self.window.set_default_size(900, 600)
self.window.show_all()
def message_dialog(self, title, message, type="info"):
types = {"info": Gtk.MessageType.INFO,
"warn": Gtk.MessageType.WARNING,
"error": Gtk.MessageType.ERROR}
dlg = Gtk.MessageDialog(self.window, 0, types[type], Gtk.ButtonsType.OK, title)
dlg.format_secondary_text(message)
dlg.run()
dlg.destroy()
def question_dialog(self, title, message):
label = Gtk.Label(message)
dialog = Gtk.Dialog(title, self.window, 0,
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_OK, Gtk.ResponseType.OK))
dialog.set_default_size(150, 100)
dialog.set_modal(True)
box = dialog.get_content_area()
box.set_border_width(10)
box.add(label)
dialog.show_all()
response = dialog.run()
dialog.destroy()
return response
def setup_toolbar(self):
# Zoom fit
@@ -255,48 +279,6 @@ class App:
"""
FlatCAMObj.app = self
# def setup_project_list(self):
# """
# Sets up list or Tree where whatever has been loaded or created is
# displayed.
#
# :return: None
# """
#
# # Model
# self.store = Gtk.ListStore(GdkPixbuf.Pixbuf, str)
# #self.store = Gtk.ListStore(str, str)
#
# # View
# self.tree = Gtk.TreeView(model=self.store)
#
# # Renderers
# renderer_pixbuf = Gtk.CellRendererPixbuf()
# column_pixbuf = Gtk.TreeViewColumn("Type", renderer_pixbuf, pixbuf=0)
# # column_pixbuf = Gtk.TreeViewColumn("Type")
# # column_pixbuf.pack_start(renderer_pixbuf, False)
# # column_pixbuf.add_attribute(renderer_pixbuf, "pixbuf", 1)
# self.tree.append_column(column_pixbuf)
#
# # renderer1 = Gtk.CellRendererText()
# # column1 = Gtk.TreeViewColumn("Type", renderer1, text=0)
# # self.tree.append_column(column1)
#
# renderer = Gtk.CellRendererText()
# column = Gtk.TreeViewColumn("Object name", renderer, text=1)
# self.tree.append_column(column)
#
# self.tree_select = self.tree.get_selection()
#
#
# self.builder.get_object("box_project").pack_start(self.tree, False, False, 1)
#
# # Double-click or Enter takes you to the object's options
# self.tree.connect("row_activated", self.on_row_activated)
#
# # Changes the selected item and populates the object options form
# self.signal_id = self.tree_select.connect("changed", self.on_tree_selection_changed)
def setup_component_editor(self):
"""
Initial configuration of the component editor. Creates
@@ -335,7 +317,7 @@ class App:
'gerber': self.open_gerber,
'excellon': self.open_excellon,
'cncjob': self.open_gcode,
'project': lambda x: ""
'project': self.open_project
}
# Closure needed to create callbacks in a loop.
@@ -378,39 +360,13 @@ class App:
def info(self, text):
"""
Show text on the status bar.
Show text on the status bar. This method is thread safe.
:param text: Text to display.
:type text: str
:return: None
"""
self.info_label.set_text(text)
# def build_list(self):
# """
# Clears and re-populates the list of objects in currently
# in the project.
#
# :return: None
# """
# icons = {
# "gerber": "share/flatcam_icon16.png",
# "excellon": "share/drill16.png",
# "cncjob": "share/cnc16.png",
# "geometry": "share/geometry16.png"
# }
#
#
# print "build_list(): clearing"
# self.tree_select.unselect_all()
# self.store.clear()
# print "repopulating...",
# for key in self.stuff:
# print key,
# obj = self.stuff[key]
# icon = GdkPixbuf.Pixbuf.new_from_file(icons[obj.kind])
# self.store.append([icon, key])
# print
GLib.idle_add(lambda: self.info_label.set_text(text))
def get_radio_value(self, radio_set):
"""
@@ -474,31 +430,11 @@ class App:
self.info("Could not evaluate: " + value)
return None
# def set_list_selection(self, name):
# """
# Marks a given object as selected in the list ob objects
# in the GUI. This selection will in turn trigger
# ``self.on_tree_selection_changed()``.
#
# :param name: Name of the object.
# :type name: str
# :return: None
# """
#
# iter = self.store.get_iter_first()
# while iter is not None and self.store[iter][1] != name:
# iter = self.store.iter_next(iter)
# self.tree_select.unselect_all()
# self.tree_select.select_iter(iter)
#
# # Need to return False such that GLib.idle_add
# # or .timeout_add do not repeat.
# return False
def new_object(self, kind, name, initialize):
"""
Creates a new specalized FlatCAMObj and attaches it to the application,
this is, updates the GUI accordingly, any other records and plots it.
This method is thread-safe.
:param kind: The kind of object to create. One of 'gerber',
'excellon', 'cncjob' and 'geometry'.
@@ -513,6 +449,8 @@ class App:
:rtype: None
"""
print "new_object()"
### Check for existing name
if name in self.collection.get_names():
## Create a new name
@@ -553,18 +491,17 @@ class App:
obj.convert_units(self.options["units"])
# Add to our records
# TODO: Perhaps make collection thread safe instead?
GLib.idle_add(lambda: self.collection.append(obj, active=True))
self.collection.append(obj, active=True)
# Show object details now.
GLib.timeout_add(100, lambda: self.notebook.set_current_page(1))
GLib.idle_add(lambda: self.notebook.set_current_page(1))
# Plot
# TODO: (Thread-safe?)
obj.plot()
# TODO: Threading dissaster!
GLib.idle_add(lambda: self.on_zoom_fit(None))
#self.on_zoom_fit(None)
return obj
@@ -582,23 +519,6 @@ class App:
self.progress_bar.set_fraction(percentage)
return False
# def get_current(self):
# """
# Returns the currently selected FlatCAMObj in the application.
#
# :return: Currently selected FlatCAMObj in the application.
# :rtype: FlatCAMObj or None
# """
#
# # TODO: Could possibly read the form into the object here.
# # But there are some cases when the form for the object
# # is not up yet. See on_tree_selection_changed.
#
# try:
# return self.stuff[self.selected_item_name]
# except:
# return None
def load_defaults(self):
"""
Loads the aplication's default settings from defaults.json into
@@ -738,6 +658,7 @@ class App:
except:
pass
# Serialize the whole project
d = {"objs": [obj.to_dict() for obj in self.collection.get_list()],
"options": self.options}
@@ -786,7 +707,7 @@ class App:
# Project options
self.options.update(d['options'])
self.project_filename = filename
self.units_label.set_text(self.options["units"])
GLib.idle_add(lambda: self.units_label.set_text(self.options["units"]))
# Re create objects
for obj in d['objs']:
@@ -961,6 +882,9 @@ class App:
########################################
## EVENT HANDLERS ##
########################################
def on_debug_printlist(self, *args):
self.collection.print_list()
def on_disable_all_plots(self, widget):
self.disable_plots()
@@ -997,8 +921,6 @@ class App:
:return: None
"""
# self.get_current().read_form()
# self.get_current().plot()
self.collection.get_active().read_form()
self.collection.get_active().plot()
@@ -1012,7 +934,6 @@ class App:
about = self.builder.get_object("aboutdialog")
response = about.run()
#about.destroy()
about.hide()
def on_create_mirror(self, widget):
@@ -1299,6 +1220,18 @@ class App:
def on_success(app_obj, filename):
assert isinstance(app_obj, App)
try:
f = open(filename, 'r')
f.close()
exists = True
except IOError:
exists = False
msg = "File exists. Overwrite?"
if exists and self.question_dialog("File exists", msg) == Gtk.ResponseType.CANCEL:
return
app_obj.save_project(filename)
self.project_filename = filename
self.register_recent("project", filename)
@@ -1319,6 +1252,18 @@ class App:
def on_success(app_obj, filename):
assert isinstance(app_obj, App)
try:
f = open(filename, 'r')
f.close()
exists = True
except IOError:
exists = False
msg = "File exists. Overwrite?"
if exists and self.question_dialog("File exists", msg) == Gtk.ResponseType.CANCEL:
return
app_obj.save_project(filename)
self.register_recent("project", filename)
app_obj.info("Project copy saved to: " + filename)
@@ -1578,9 +1523,7 @@ class App:
obj.plot()
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()
# Send to worker
self.worker.add_task(thread_func, [self])
def on_generate_excellon_cncjob(self, widget):
@@ -1625,10 +1568,7 @@ class App:
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, ""))
# Start the thread
# t = threading.Thread(target=job_thread, args=(self,))
# t.daemon = True
# t.start()
# Send to worker
self.worker.add_task(job_thread, [self])
def on_excellon_tool_choose(self, widget):
@@ -1818,10 +1758,7 @@ class App:
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, ""))
# Start the thread
# t = threading.Thread(target=job_thread, args=(self,))
# t.daemon = True
# t.start()
# Send to worker
self.worker.add_task(job_thread, [self])
def on_generate_paintarea(self, widget):
@@ -1843,10 +1780,14 @@ class App:
tooldia = geo.options["painttooldia"]
overlap = geo.options["paintoverlap"]
# Connection ID for the click event
subscription = None
# To be called after clicking on the plot.
def doit(event):
self.plot_click_subscribers.pop("generate_paintarea")
self.info("")
#self.plot_click_subscribers.pop("generate_paintarea")
self.plotcanvas.mpl_disconnect(subscription)
self.info("Painting")
point = [event.xdata, event.ydata]
poly = find_polygon(geo.solid_geometry, point)
@@ -1858,10 +1799,12 @@ class App:
geo_obj.solid_geometry = cp
geo_obj.options["cnctooldia"] = tooldia
name = self.selected_item_name + "_paint"
#name = self.selected_item_name + "_paint"
name = geo.options["name"] + "_paint"
self.new_object("geometry", name, gen_paintarea)
self.plot_click_subscribers["generate_paintarea"] = doit
#self.plot_click_subscribers["generate_paintarea"] = doit
subscription = self.plotcanvas.mpl_connect('button_press_event', doit)
def on_cncjob_exportgcode(self, widget):
"""
@@ -1940,23 +1883,25 @@ class App:
def on_file_new(self, param):
"""
Callback for menu item File->New. Returns the application to its
startup state.
startup state. This method is thread-safe.
:param param: Whatever is passed by the event. Ignore.
:return: None
"""
# Remove everything from memory
# Clear plot
self.plotcanvas.clear()
# Delete data
self.collection.delete_all()
# GUI things
def task():
# Clear plot
self.plotcanvas.clear()
# Clear object editor
self.setup_component_editor()
# Delete data
self.collection.delete_all()
# Clear list
#self.collection.build_list()
# Clear object editor
self.setup_component_editor()
GLib.idle_add(task)
# Clear project filename
self.project_filename = None
@@ -2006,13 +1951,10 @@ class App:
if response == Gtk.ResponseType.OK:
filename = dialog.get_filename()
dialog.destroy()
# t = threading.Thread(target=on_success, args=(self, filename))
# t.daemon = True
# t.start()
# Send to worker.
self.worker.add_task(on_success, [self, filename])
#on_success(self, filename)
elif response == Gtk.ResponseType.CANCEL:
self.info("Open cancelled.") # print("Cancel clicked")
self.info("Open cancelled.")
dialog.destroy()
def file_chooser_save_action(self, on_success):
@@ -2043,8 +1985,12 @@ class App:
def obj_init(gerber_obj, app_obj):
assert isinstance(gerber_obj, FlatCAMGerber)
# Opening the file happens here
GLib.idle_add(lambda: app_obj.set_progress_bar(0.2, "Parsing ..."))
gerber_obj.parse_file(filename)
# Further parsing
GLib.idle_add(lambda: app_obj.set_progress_bar(0.5, "Creating Geometry ..."))
gerber_obj.create_geometry()
GLib.idle_add(lambda: app_obj.set_progress_bar(0.6, "Plotting ..."))
@@ -2053,8 +1999,9 @@ class App:
self.new_object("gerber", name, obj_init)
self.register_recent("gerber", filename)
self.info("Opened: " + filename)
GLib.idle_add(lambda: self.set_progress_bar(1.0, "Done!"))
GLib.timeout_add_seconds(1, lambda: self.set_progress_bar(0.0, ""))
GLib.timeout_add_seconds(1, lambda: self.set_progress_bar(0.0, "Idle"))
def on_fileopengerber(self, param):
"""
@@ -2065,30 +2012,7 @@ class App:
:param param: Ignore
:return: None
"""
# IMPORTANT: on_success will run on a separate thread. Use
# GLib.idle_add(function, **kwargs) to launch actions that will
# updata the GUI.
# def on_success(app_obj, filename):
# assert isinstance(app_obj, App)
# GLib.idle_add(lambda: app_obj.set_progress_bar(0.1, "Opening Gerber ..."))
#
# def obj_init(gerber_obj, app_obj):
# assert isinstance(gerber_obj, FlatCAMGerber)
# GLib.idle_add(lambda: app_obj.set_progress_bar(0.2, "Parsing ..."))
# gerber_obj.parse_file(filename)
# GLib.idle_add(lambda: app_obj.set_progress_bar(0.5, "Creating Geometry ..."))
# gerber_obj.create_geometry()
# GLib.idle_add(lambda: app_obj.set_progress_bar(0.6, "Plotting ..."))
#
# name = filename.split('/')[-1].split('\\')[-1]
# app_obj.new_object("gerber", name, obj_init)
# app_obj.register_recent("gerber", filename)
#
# 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, ""))
# on_success gets run on a separate thread
# self.file_chooser_action(on_success)
self.file_chooser_action(lambda ao, filename: self.open_gerber(filename))
def open_excellon(self, filename):
@@ -2104,6 +2028,7 @@ class App:
self.new_object("excellon", name, obj_init)
self.register_recent("excellon", filename)
self.info("Opened: " + filename)
GLib.idle_add(lambda: self.set_progress_bar(1.0, "Done!"))
GLib.timeout_add_seconds(1, lambda: self.set_progress_bar(0.0, ""))
@@ -2116,28 +2041,7 @@ class App:
:param param: Ignore
:return: None
"""
# IMPORTANT: on_success will run on a separate thread. Use
# GLib.idle_add(function, **kwargs) to launch actions that will
# updata the GUI.
# def on_success(app_obj, filename):
# assert isinstance(app_obj, App)
# GLib.idle_add(lambda: app_obj.set_progress_bar(0.1, "Opening Excellon ..."))
#
# def obj_init(excellon_obj, app_obj):
# GLib.idle_add(lambda: app_obj.set_progress_bar(0.2, "Parsing ..."))
# excellon_obj.parse_file(filename)
# excellon_obj.create_geometry()
# GLib.idle_add(lambda: app_obj.set_progress_bar(0.6, "Plotting ..."))
#
# name = filename.split('/')[-1].split('\\')[-1]
# app_obj.new_object("excellon", name, obj_init)
# self.register_recent("excellon", filename)
#
# 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, ""))
# on_success gets run on a separate thread
# self.file_chooser_action(on_success)
self.file_chooser_action(lambda ao, filename: self.open_excellon(filename))
def open_gcode(self, filename):
@@ -2168,6 +2072,7 @@ class App:
self.new_object("cncjob", name, obj_init)
self.register_recent("cncjob", filename)
self.info("Opened: " + filename)
GLib.idle_add(lambda: self.set_progress_bar(1.0, "Done!"))
GLib.timeout_add_seconds(1, lambda: self.set_progress_bar(0.0, ""))
@@ -2180,43 +2085,7 @@ class App:
:param param: Ignore
:return: None
"""
# IMPORTANT: on_success will run on a separate thread. Use
# GLib.idle_add(function, **kwargs) to launch actions that will
# updata the GUI.
# def on_success(app_obj, filename):
# assert isinstance(app_obj, App)
#
# def obj_init(job_obj, app_obj_):
# """
#
# :type app_obj_: App
# """
# assert isinstance(app_obj_, App)
# GLib.idle_add(lambda: app_obj_.set_progress_bar(0.1, "Opening G-Code ..."))
#
# f = open(filename)
# gcode = f.read()
# f.close()
#
# job_obj.gcode = gcode
#
# GLib.idle_add(lambda: app_obj_.set_progress_bar(0.2, "Parsing ..."))
# job_obj.gcode_parse()
#
# GLib.idle_add(lambda: app_obj_.set_progress_bar(0.6, "Creating geometry ..."))
# job_obj.create_geometry()
#
# GLib.idle_add(lambda: app_obj_.set_progress_bar(0.6, "Plotting ..."))
#
# name = filename.split('/')[-1].split('\\')[-1]
# app_obj.new_object("cncjob", name, obj_init)
# self.register_recent("cncjob", filename)
#
# 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, ""))
# on_success gets run on a separate thread
# self.file_chooser_action(on_success)
self.file_chooser_action(lambda ao, filename: self.open_gcode(filename))
def on_mouse_move_over_plot(self, event):
@@ -2234,8 +2103,8 @@ class App:
event.xdata, event.ydata))
self.mouse = [event.xdata, event.ydata]
for subscriber in self.plot_mousemove_subscribers:
self.plot_mousemove_subscribers[subscriber](event)
# for subscriber in self.plot_mousemove_subscribers:
# self.plot_mousemove_subscribers[subscriber](event)
except:
self.position_label.set_label("")
@@ -2256,7 +2125,7 @@ class App:
:return: None
"""
# For key presses
# So it can receive key presses
self.plotcanvas.canvas.grab_focus()
try:
@@ -2264,8 +2133,8 @@ class App:
event.button, event.x, event.y, event.xdata, event.ydata)
# TODO: This custom subscription mechanism is probably not necessary.
for subscriber in self.plot_click_subscribers:
self.plot_click_subscribers[subscriber](event)
# for subscriber in self.plot_click_subscribers:
# self.plot_click_subscribers[subscriber](event)
self.clipboard.set_text("(%.4f, %.4f)" % (event.xdata, event.ydata), -1)
@@ -2403,31 +2272,32 @@ class DrawingPoint(DrawingObject):
class Measurement:
def __init__(self, container, axes, click_subscibers, move_subscribers, update=None):
def __init__(self, container, plotcanvas, update=None):
self.update = update
self.container = container
self.frame = None
self.label = None
self.click_subscribers = click_subscibers
self.move_subscribers = move_subscribers
self.point1 = None
self.point2 = None
self.active = False
self.plotcanvas = plotcanvas
self.click_subscription = None
self.move_subscription = None
def toggle_active(self, *args):
if self.active: # Deactivate
self.active = False
self.move_subscribers.pop("meas")
self.click_subscribers.pop("meas")
self.container.remove(self.frame)
if self.update is not None:
self.update()
self.plotcanvas.mpl_disconnect(self.click_subscription)
self.plotcanvas.mpl_disconnect(self.move_subscription)
return False
else: # Activate
print "DEBUG: Activating Measurement Tool..."
self.active = True
self.click_subscribers["meas"] = self.on_click
self.move_subscribers["meas"] = self.on_move
self.click_subscription = self.plotcanvas.mpl_connect("button_press_event", self.on_click)
self.move_subscription = self.plotcanvas.mpl_connect('motion_notify_event', self.on_move)
self.frame = Gtk.Frame()
self.frame.set_margin_right(5)
self.frame.set_margin_top(3)
@@ -2449,10 +2319,13 @@ class Measurement:
if self.point1 is None:
self.label.set_label("Click on a reference point...")
else:
dx = event.xdata - self.point1[0]
dy = event.ydata - self.point1[1]
d = sqrt(dx**2 + dy**2)
self.label.set_label("D = %.4f D(x) = %.4f D(y) = %.4f" % (d, dx, dy))
try:
dx = event.xdata - self.point1[0]
dy = event.ydata - self.point1[1]
d = sqrt(dx**2 + dy**2)
self.label.set_label("D = %.4f D(x) = %.4f D(y) = %.4f" % (d, dx, dy))
except TypeError:
pass
if self.update is not None:
self.update()
@@ -2540,9 +2413,18 @@ class PlotCanvas:
:type event_name: str
:param callback: Function to call
:type callback: func
:return: Nothing
:return: Connection id
:rtype: int
"""
self.canvas.mpl_connect(event_name, callback)
return self.canvas.mpl_connect(event_name, callback)
def mpl_disconnect(self, cid):
"""
Disconnect callback with the give id.
:param cid: Callback id.
:return: None
"""
self.canvas.mpl_disconnect(cid)
def connect(self, event_name, callback):
"""
@@ -2592,6 +2474,8 @@ class PlotCanvas:
:return: None
"""
print "PC.adjust_axes()"
width = xmax - xmin
height = ymax - ymin
try:
@@ -2707,21 +2591,6 @@ class PlotCanvas:
return self.figure.add_axes([0.05, 0.05, 0.9, 0.9], label=name)
# def plot_axes(self, axes):
#
# if axes not in self.figure.axes:
# self.figure.add_axes(axes)
#
# # Basic configuration
# axes.set_frame_on(False) # No frame
# axes.set_xticks([]) # No tick
# axes.set_yticks([]) # No ticks
# axes.patch.set_visible(False) # No background
# axes.set_aspect(1)
#
# # Adjust limits
# self.auto_adjust_axes()
def on_scroll(self, canvas, event):
"""
Scroll event handler.
@@ -2730,6 +2599,11 @@ class PlotCanvas:
:param event: Event object containing the event information.
:return: None
"""
# So it can receive key presses
self.canvas.grab_focus()
# Event info
z, direction = event.get_scroll_direction()
if self.key is None:
@@ -2758,7 +2632,7 @@ class PlotCanvas:
def on_mouse_move(self, event):
"""
Mouse movement event hadler.
Mouse movement event hadler. Stores the coordinates.
:param event: Contains information about the event.
:return: None
@@ -2822,6 +2696,13 @@ class ObjectCollection:
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.collection = []
@@ -2837,10 +2718,22 @@ class ObjectCollection:
pass
def on_row_activated(self, *args):
"""
Does nothing right now.
:param args: Ignored.
:return: None
"""
print "OC.on_row_activated()"
return
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 "OC.on_list_selection_change()"
try:
self.get_active().build_ui()
@@ -2851,6 +2744,14 @@ class ObjectCollection:
# TODO: active, so cannot read form.
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)
@@ -2863,6 +2764,13 @@ class ObjectCollection:
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 "OC.set_list_selection()"
iterat = self.store.get_iter_first()
while iterat is not None and self.store[iterat][0].options["name"] != name:
@@ -2870,13 +2778,30 @@ class ObjectCollection:
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 "OC.append()"
self.store.append([obj])
if active:
self.set_list_selection(obj.options["name"])
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()
@@ -2887,6 +2812,12 @@ class ObjectCollection:
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.
@@ -2911,6 +2842,12 @@ class ObjectCollection:
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:
@@ -2920,6 +2857,14 @@ class ObjectCollection:
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]
@@ -2929,6 +2874,17 @@ class ObjectCollection:
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
"""
iterat = self.store.get_iter_first()
while iterat is not None:
obj = self.store[iterat][0]