Created class ObjectCollection to manage the list of objects in the program. Converted the program to use it. Not fully functional yet.
This commit is contained in:
562
FlatCAM.py
562
FlatCAM.py
@@ -23,14 +23,17 @@ from numpy import arange, sin, pi
|
|||||||
from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas
|
from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas
|
||||||
#from mpl_toolkits.axes_grid.anchored_artists import AnchoredText
|
#from mpl_toolkits.axes_grid.anchored_artists import AnchoredText
|
||||||
|
|
||||||
from camlib import *
|
|
||||||
import sys
|
import sys
|
||||||
import urllib
|
import urllib
|
||||||
import copy
|
import copy
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
########################################
|
||||||
|
## Imports part of FlatCAM ##
|
||||||
|
########################################
|
||||||
|
from camlib import *
|
||||||
from FlatCAMObj import *
|
from FlatCAMObj import *
|
||||||
|
|
||||||
from FlatCAMWorker import Worker
|
from FlatCAMWorker import Worker
|
||||||
|
|
||||||
########################################
|
########################################
|
||||||
@@ -41,6 +44,8 @@ class App:
|
|||||||
The main application class. The constructor starts the GUI.
|
The main application class. The constructor starts the GUI.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
version_url = "http://caram.cl/flatcam/VERSION"
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""
|
"""
|
||||||
Starts the application. Takes no parameters.
|
Starts the application. Takes no parameters.
|
||||||
@@ -78,7 +83,7 @@ class App:
|
|||||||
self.combo_options = self.builder.get_object("combo_options")
|
self.combo_options = self.builder.get_object("combo_options")
|
||||||
self.combo_options.set_active(1)
|
self.combo_options.set_active(1)
|
||||||
|
|
||||||
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.
|
## Setup the toolbar. Adds buttons.
|
||||||
@@ -95,16 +100,20 @@ class App:
|
|||||||
|
|
||||||
self.setup_tooltips()
|
self.setup_tooltips()
|
||||||
|
|
||||||
|
# TODO: Hardcoded list
|
||||||
|
for kind in ['gerber', 'excellon', 'geometry', 'cncjob']:
|
||||||
|
entry_name = self.builder.get_object("entry_text_" + kind + "_name")
|
||||||
|
entry_name.connect("activate", self.on_activate_name)
|
||||||
|
|
||||||
#### DATA ####
|
#### DATA ####
|
||||||
self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
||||||
self.setup_obj_classes()
|
self.setup_obj_classes()
|
||||||
self.stuff = {} # FlatCAMObj's by name
|
|
||||||
self.mouse = None # Mouse coordinates over plot
|
self.mouse = None # Mouse coordinates over plot
|
||||||
self.recent = []
|
self.recent = []
|
||||||
|
self.collection = ObjectCollection()
|
||||||
# What is selected by the user. It is
|
self.builder.get_object("box_project").pack_start(self.collection.view, False, False, 1)
|
||||||
# a key if self.stuff
|
# TODO: Do this different
|
||||||
self.selected_item_name = None
|
self.collection.view.connect("row_activated", self.on_row_activated)
|
||||||
|
|
||||||
# Used to inhibit the on_options_update callback when
|
# Used to inhibit the on_options_update callback when
|
||||||
# the options are being changed by the program and not the user.
|
# the options are being changed by the program and not the user.
|
||||||
@@ -140,6 +149,7 @@ class App:
|
|||||||
# self.radios_inv.update({obj.kind + "_" + option: obj.radios_inv[option]})
|
# self.radios_inv.update({obj.kind + "_" + option: obj.radios_inv[option]})
|
||||||
|
|
||||||
## Event subscriptions ##
|
## Event subscriptions ##
|
||||||
|
# TODO: Move to plotcanvas
|
||||||
self.plot_click_subscribers = {}
|
self.plot_click_subscribers = {}
|
||||||
self.plot_mousemove_subscribers = {}
|
self.plot_mousemove_subscribers = {}
|
||||||
|
|
||||||
@@ -168,11 +178,11 @@ class App:
|
|||||||
self.worker.start()
|
self.worker.start()
|
||||||
|
|
||||||
#### Check for updates ####
|
#### Check for updates ####
|
||||||
|
# Separate thread (Not worker)
|
||||||
self.version = 4
|
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):
|
||||||
@@ -245,27 +255,47 @@ class App:
|
|||||||
"""
|
"""
|
||||||
FlatCAMObj.app = self
|
FlatCAMObj.app = self
|
||||||
|
|
||||||
def setup_project_list(self):
|
# def setup_project_list(self):
|
||||||
"""
|
# """
|
||||||
Sets up list or Tree where whatever has been loaded or created is
|
# Sets up list or Tree where whatever has been loaded or created is
|
||||||
displayed.
|
# displayed.
|
||||||
|
#
|
||||||
:return: None
|
# :return: None
|
||||||
"""
|
# """
|
||||||
|
#
|
||||||
self.store = Gtk.ListStore(str)
|
# # Model
|
||||||
self.tree = Gtk.TreeView(self.store)
|
# self.store = Gtk.ListStore(GdkPixbuf.Pixbuf, str)
|
||||||
self.tree_select = self.tree.get_selection()
|
# #self.store = Gtk.ListStore(str, str)
|
||||||
renderer = Gtk.CellRendererText()
|
#
|
||||||
column = Gtk.TreeViewColumn("Objects", renderer, text=0)
|
# # View
|
||||||
self.tree.append_column(column)
|
# self.tree = Gtk.TreeView(model=self.store)
|
||||||
self.builder.get_object("box_project").pack_start(self.tree, False, False, 1)
|
#
|
||||||
|
# # Renderers
|
||||||
# Double-click or Enter takes you to the object's options
|
# renderer_pixbuf = Gtk.CellRendererPixbuf()
|
||||||
self.tree.connect("row_activated", self.on_row_activated)
|
# column_pixbuf = Gtk.TreeViewColumn("Type", renderer_pixbuf, pixbuf=0)
|
||||||
|
# # column_pixbuf = Gtk.TreeViewColumn("Type")
|
||||||
# Changes the selected item and populates the object options form
|
# # column_pixbuf.pack_start(renderer_pixbuf, False)
|
||||||
self.signal_id = self.tree_select.connect("changed", self.on_tree_selection_changed)
|
# # 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):
|
def setup_component_editor(self):
|
||||||
"""
|
"""
|
||||||
@@ -293,6 +323,7 @@ class App:
|
|||||||
|
|
||||||
def setup_recent_items(self):
|
def setup_recent_items(self):
|
||||||
|
|
||||||
|
# TODO: Move this to constructor
|
||||||
icons = {
|
icons = {
|
||||||
"gerber": "share/flatcam_icon16.png",
|
"gerber": "share/flatcam_icon16.png",
|
||||||
"excellon": "share/drill16.png",
|
"excellon": "share/drill16.png",
|
||||||
@@ -309,13 +340,6 @@ 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):
|
||||||
self.worker.add_task(func, [fname])
|
self.worker.add_task(func, [fname])
|
||||||
@@ -362,21 +386,31 @@ class App:
|
|||||||
"""
|
"""
|
||||||
self.info_label.set_text(text)
|
self.info_label.set_text(text)
|
||||||
|
|
||||||
def build_list(self):
|
# def build_list(self):
|
||||||
"""
|
# """
|
||||||
Clears and re-populates the list of objects in currently
|
# Clears and re-populates the list of objects in currently
|
||||||
in the project.
|
# in the project.
|
||||||
|
#
|
||||||
:return: None
|
# :return: None
|
||||||
"""
|
# """
|
||||||
print "build_list(): clearing"
|
# icons = {
|
||||||
self.tree_select.unselect_all()
|
# "gerber": "share/flatcam_icon16.png",
|
||||||
self.store.clear()
|
# "excellon": "share/drill16.png",
|
||||||
print "repopulating...",
|
# "cncjob": "share/cnc16.png",
|
||||||
for key in self.stuff:
|
# "geometry": "share/geometry16.png"
|
||||||
print key,
|
# }
|
||||||
self.store.append([key])
|
#
|
||||||
print
|
#
|
||||||
|
# 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
|
||||||
|
|
||||||
def get_radio_value(self, radio_set):
|
def get_radio_value(self, radio_set):
|
||||||
"""
|
"""
|
||||||
@@ -401,26 +435,24 @@ class App:
|
|||||||
self.plotcanvas.clear()
|
self.plotcanvas.clear()
|
||||||
self.set_progress_bar(0.1, "Re-plotting...")
|
self.set_progress_bar(0.1, "Re-plotting...")
|
||||||
|
|
||||||
def thread_func(app_obj):
|
def worker_task(app_obj):
|
||||||
percentage = 0.1
|
percentage = 0.1
|
||||||
try:
|
try:
|
||||||
delta = 0.9 / len(self.stuff)
|
delta = 0.9 / len(self.collection.get_list())
|
||||||
except ZeroDivisionError:
|
except ZeroDivisionError:
|
||||||
GLib.timeout_add(300, lambda: app_obj.set_progress_bar(0.0, ""))
|
GLib.timeout_add(300, lambda: app_obj.set_progress_bar(0.0, ""))
|
||||||
return
|
return
|
||||||
for i in self.stuff:
|
for obj in self.collection.get_list():
|
||||||
self.stuff[i].plot()
|
obj.plot()
|
||||||
percentage += delta
|
percentage += delta
|
||||||
GLib.idle_add(lambda: app_obj.set_progress_bar(percentage, "Re-plotting..."))
|
GLib.idle_add(lambda: app_obj.set_progress_bar(percentage, "Re-plotting..."))
|
||||||
|
|
||||||
GLib.idle_add(app_obj.plotcanvas.auto_adjust_axes)
|
GLib.idle_add(app_obj.plotcanvas.auto_adjust_axes)
|
||||||
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, "Idle"))
|
||||||
|
|
||||||
# t = threading.Thread(target=thread_func, args=(self,))
|
# Send to worker
|
||||||
# t.daemon = True
|
self.worker.add_task(worker_task, [self])
|
||||||
# t.start()
|
|
||||||
self.worker.add_task(thread_func, [self])
|
|
||||||
|
|
||||||
def get_eval(self, widget_name):
|
def get_eval(self, widget_name):
|
||||||
"""
|
"""
|
||||||
@@ -442,26 +474,26 @@ class App:
|
|||||||
self.info("Could not evaluate: " + value)
|
self.info("Could not evaluate: " + value)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def set_list_selection(self, name):
|
# def set_list_selection(self, name):
|
||||||
"""
|
# """
|
||||||
Marks a given object as selected in the list ob objects
|
# Marks a given object as selected in the list ob objects
|
||||||
in the GUI. This selection will in turn trigger
|
# in the GUI. This selection will in turn trigger
|
||||||
``self.on_tree_selection_changed()``.
|
# ``self.on_tree_selection_changed()``.
|
||||||
|
#
|
||||||
:param name: Name of the object.
|
# :param name: Name of the object.
|
||||||
:type name: str
|
# :type name: str
|
||||||
:return: None
|
# :return: None
|
||||||
"""
|
# """
|
||||||
|
#
|
||||||
iter = self.store.get_iter_first()
|
# iter = self.store.get_iter_first()
|
||||||
while iter is not None and self.store[iter][0] != name:
|
# while iter is not None and self.store[iter][1] != name:
|
||||||
iter = self.store.iter_next(iter)
|
# iter = self.store.iter_next(iter)
|
||||||
self.tree_select.unselect_all()
|
# self.tree_select.unselect_all()
|
||||||
self.tree_select.select_iter(iter)
|
# self.tree_select.select_iter(iter)
|
||||||
|
#
|
||||||
# Need to return False such that GLib.idle_add
|
# # Need to return False such that GLib.idle_add
|
||||||
# or .timeout_add do not repeat.
|
# # or .timeout_add do not repeat.
|
||||||
return False
|
# return False
|
||||||
|
|
||||||
def new_object(self, kind, name, initialize):
|
def new_object(self, kind, name, initialize):
|
||||||
"""
|
"""
|
||||||
@@ -482,7 +514,7 @@ class App:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
### Check for existing name
|
### Check for existing name
|
||||||
if name in self.stuff:
|
if name in self.collection.get_names():
|
||||||
## Create a new name
|
## Create a new name
|
||||||
# Ends with number?
|
# Ends with number?
|
||||||
match = re.search(r'(.*[^\d])?(\d+)$', name)
|
match = re.search(r'(.*[^\d])?(\d+)$', name)
|
||||||
@@ -503,12 +535,6 @@ class App:
|
|||||||
obj = classdict[kind](name)
|
obj = classdict[kind](name)
|
||||||
obj.units = self.options["units"] # TODO: The constructor should look at defaults.
|
obj.units = self.options["units"] # TODO: The constructor should look at defaults.
|
||||||
|
|
||||||
# Initialize as per user request
|
|
||||||
# User must take care to implement initialize
|
|
||||||
# in a thread-safe way as is is likely that we
|
|
||||||
# have been invoked in a separate thread.
|
|
||||||
#initialize(obj, self)
|
|
||||||
|
|
||||||
# Set default options from self.options
|
# Set default options from self.options
|
||||||
for option in self.options:
|
for option in self.options:
|
||||||
if option.find(kind + "_") == 0:
|
if option.find(kind + "_") == 0:
|
||||||
@@ -527,14 +553,10 @@ class App:
|
|||||||
obj.convert_units(self.options["units"])
|
obj.convert_units(self.options["units"])
|
||||||
|
|
||||||
# Add to our records
|
# Add to our records
|
||||||
self.stuff[name] = obj
|
# TODO: Perhaps make collection thread safe instead?
|
||||||
|
GLib.idle_add(lambda: self.collection.append(obj, active=True))
|
||||||
|
|
||||||
# Update GUI list and select it (Thread-safe?)
|
# Show object details now.
|
||||||
self.store.append([name])
|
|
||||||
#self.build_list()
|
|
||||||
GLib.idle_add(lambda: self.set_list_selection(name))
|
|
||||||
# TODO: Gtk.notebook.set_current_page is not known to
|
|
||||||
# TODO: return False. Fix this??
|
|
||||||
GLib.timeout_add(100, lambda: self.notebook.set_current_page(1))
|
GLib.timeout_add(100, lambda: self.notebook.set_current_page(1))
|
||||||
|
|
||||||
# Plot
|
# Plot
|
||||||
@@ -560,22 +582,22 @@ class App:
|
|||||||
self.progress_bar.set_fraction(percentage)
|
self.progress_bar.set_fraction(percentage)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_current(self):
|
# def get_current(self):
|
||||||
"""
|
# """
|
||||||
Returns the currently selected FlatCAMObj in the application.
|
# Returns the currently selected FlatCAMObj in the application.
|
||||||
|
#
|
||||||
:return: Currently selected FlatCAMObj in the application.
|
# :return: Currently selected FlatCAMObj in the application.
|
||||||
:rtype: FlatCAMObj or None
|
# :rtype: FlatCAMObj or None
|
||||||
"""
|
# """
|
||||||
|
#
|
||||||
# TODO: Could possibly read the form into the object here.
|
# # TODO: Could possibly read the form into the object here.
|
||||||
# But there are some cases when the form for the object
|
# # But there are some cases when the form for the object
|
||||||
# is not up yet. See on_tree_selection_changed.
|
# # is not up yet. See on_tree_selection_changed.
|
||||||
|
#
|
||||||
try:
|
# try:
|
||||||
return self.stuff[self.selected_item_name]
|
# return self.stuff[self.selected_item_name]
|
||||||
except:
|
# except:
|
||||||
return None
|
# return None
|
||||||
|
|
||||||
def load_defaults(self):
|
def load_defaults(self):
|
||||||
"""
|
"""
|
||||||
@@ -712,11 +734,11 @@ class App:
|
|||||||
|
|
||||||
# Capture the latest changes
|
# Capture the latest changes
|
||||||
try:
|
try:
|
||||||
self.get_current().read_form()
|
self.collection.get_active().read_form()
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
d = {"objs": [self.stuff[o].to_dict() for o in self.stuff],
|
d = {"objs": [obj.to_dict() for obj in self.collection.get_list()],
|
||||||
"options": self.options}
|
"options": self.options}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -746,15 +768,12 @@ class App:
|
|||||||
try:
|
try:
|
||||||
f = open(filename, 'r')
|
f = open(filename, 'r')
|
||||||
except:
|
except:
|
||||||
#print "WARNING: Failed to open project file:", filename
|
|
||||||
self.info("ERROR: Failed to open project file: %s" % filename)
|
self.info("ERROR: Failed to open project file: %s" % filename)
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
d = json.load(f, object_hook=dict2obj)
|
d = json.load(f, object_hook=dict2obj)
|
||||||
except:
|
except:
|
||||||
#print sys.exc_info()
|
|
||||||
#print "WARNING: Failed to parse project file:", filename
|
|
||||||
self.info("ERROR: Failed to parse project file: %s" % filename)
|
self.info("ERROR: Failed to parse project file: %s" % filename)
|
||||||
f.close()
|
f.close()
|
||||||
return
|
return
|
||||||
@@ -790,8 +809,8 @@ class App:
|
|||||||
combo = self.builder.get_object(combo)
|
combo = self.builder.get_object(combo)
|
||||||
|
|
||||||
combo.remove_all()
|
combo.remove_all()
|
||||||
for obj in self.stuff:
|
for name in self.collection.get_names():
|
||||||
combo.append_text(obj)
|
combo.append_text(name)
|
||||||
|
|
||||||
def versionCheck(self, *args):
|
def versionCheck(self, *args):
|
||||||
"""
|
"""
|
||||||
@@ -803,7 +822,7 @@ class App:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
f = urllib.urlopen("http://caram.cl/flatcam/VERSION") # TODO: Hardcoded.
|
f = urllib.urlopen(App.version_url)
|
||||||
except:
|
except:
|
||||||
GLib.idle_add(lambda: self.info("ERROR trying to check for latest version."))
|
GLib.idle_add(lambda: self.info("ERROR trying to check for latest version."))
|
||||||
return
|
return
|
||||||
@@ -868,52 +887,49 @@ class App:
|
|||||||
|
|
||||||
self.set_progress_bar(0.1, "Re-plotting...")
|
self.set_progress_bar(0.1, "Re-plotting...")
|
||||||
|
|
||||||
def thread_func(app_obj):
|
def worker_task(app_obj):
|
||||||
percentage = 0.1
|
percentage = 0.1
|
||||||
try:
|
try:
|
||||||
delta = 0.9 / len(self.stuff)
|
delta = 0.9 / len(self.collection.get_list())
|
||||||
except ZeroDivisionError:
|
except ZeroDivisionError:
|
||||||
GLib.timeout_add(300, lambda: app_obj.set_progress_bar(0.0, ""))
|
GLib.timeout_add(300, lambda: app_obj.set_progress_bar(0.0, ""))
|
||||||
return
|
return
|
||||||
for i in self.stuff:
|
for obj in self.collection.get_list():
|
||||||
if i != app_obj.selected_item_name or not except_current:
|
#if i != app_obj.selected_item_name or not except_current:
|
||||||
self.stuff[i].options['plot'] = False
|
if obj != self.collection.get_active() or not except_current:
|
||||||
self.stuff[i].plot()
|
obj.options['plot'] = False
|
||||||
|
obj.plot()
|
||||||
percentage += delta
|
percentage += delta
|
||||||
GLib.idle_add(lambda: app_obj.set_progress_bar(percentage, "Re-plotting..."))
|
GLib.idle_add(lambda: app_obj.set_progress_bar(percentage, "Re-plotting..."))
|
||||||
|
|
||||||
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,))
|
# Send to worker
|
||||||
# t.daemon = True
|
self.worker.add_task(worker_task, [self])
|
||||||
# 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()
|
||||||
self.set_progress_bar(0.1, "Re-plotting...")
|
self.set_progress_bar(0.1, "Re-plotting...")
|
||||||
|
|
||||||
def thread_func(app_obj):
|
def worker_task(app_obj):
|
||||||
percentage = 0.1
|
percentage = 0.1
|
||||||
try:
|
try:
|
||||||
delta = 0.9 / len(self.stuff)
|
delta = 0.9 / len(self.collection.get_list())
|
||||||
except ZeroDivisionError:
|
except ZeroDivisionError:
|
||||||
GLib.timeout_add(300, lambda: app_obj.set_progress_bar(0.0, ""))
|
GLib.timeout_add(300, lambda: app_obj.set_progress_bar(0.0, ""))
|
||||||
return
|
return
|
||||||
for i in self.stuff:
|
for obj in self.collection.get_list():
|
||||||
self.stuff[i].options['plot'] = True
|
obj.options['plot'] = True
|
||||||
self.stuff[i].plot()
|
obj.plot()
|
||||||
percentage += delta
|
percentage += delta
|
||||||
GLib.idle_add(lambda: app_obj.set_progress_bar(percentage, "Re-plotting..."))
|
GLib.idle_add(lambda: app_obj.set_progress_bar(percentage, "Re-plotting..."))
|
||||||
|
|
||||||
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,))
|
# Send to worker
|
||||||
# t.daemon = True
|
self.worker.add_task(worker_task, [self])
|
||||||
# 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}
|
||||||
@@ -960,7 +976,7 @@ class App:
|
|||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
obj = self.get_current()
|
obj = self.collection.get_active()
|
||||||
obj.read_form()
|
obj.read_form()
|
||||||
assert isinstance(obj, FlatCAMObj)
|
assert isinstance(obj, FlatCAMObj)
|
||||||
try:
|
try:
|
||||||
@@ -981,8 +997,10 @@ class App:
|
|||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.get_current().read_form()
|
# self.get_current().read_form()
|
||||||
self.get_current().plot()
|
# self.get_current().plot()
|
||||||
|
self.collection.get_active().read_form()
|
||||||
|
self.collection.get_active().plot()
|
||||||
|
|
||||||
def on_about(self, widget):
|
def on_about(self, widget):
|
||||||
"""
|
"""
|
||||||
@@ -1007,12 +1025,8 @@ class App:
|
|||||||
# TODO: Move (some of) this to camlib!
|
# TODO: Move (some of) this to camlib!
|
||||||
|
|
||||||
# Object to mirror
|
# Object to mirror
|
||||||
try:
|
obj_name = self.builder.get_object("comboboxtext_bottomlayer").get_active_text()
|
||||||
obj_name = self.builder.get_object("comboboxtext_bottomlayer").get_active_text()
|
fcobj = self.collection.get_by_name(obj_name)
|
||||||
fcobj = self.stuff[obj_name]
|
|
||||||
except KeyError:
|
|
||||||
self.info("WARNING: Cannot mirror that object.")
|
|
||||||
return
|
|
||||||
|
|
||||||
# For now, lets limit to Gerbers and Excellons.
|
# For now, lets limit to Gerbers and Excellons.
|
||||||
# assert isinstance(gerb, FlatCAMGerber)
|
# assert isinstance(gerb, FlatCAMGerber)
|
||||||
@@ -1030,23 +1044,13 @@ class App:
|
|||||||
px, py = eval(self.point_entry.get_text())
|
px, py = eval(self.point_entry.get_text())
|
||||||
else: # The axis is the line dividing the box in the middle
|
else: # The axis is the line dividing the box in the middle
|
||||||
name = self.box_combo.get_active_text()
|
name = self.box_combo.get_active_text()
|
||||||
bb_obj = self.stuff[name]
|
bb_obj = self.collection.get_by_name(name)
|
||||||
xmin, ymin, xmax, ymax = bb_obj.bounds()
|
xmin, ymin, xmax, ymax = bb_obj.bounds()
|
||||||
px = 0.5*(xmin+xmax)
|
px = 0.5*(xmin+xmax)
|
||||||
py = 0.5*(ymin+ymax)
|
py = 0.5*(ymin+ymax)
|
||||||
|
|
||||||
# Do the mirroring
|
|
||||||
# xscale, yscale = {"X": (1.0, -1.0), "Y": (-1.0, 1.0)}[axis]
|
|
||||||
# mirrored = affinity.scale(fcobj.solid_geometry, xscale, yscale, origin=(px, py))
|
|
||||||
#
|
|
||||||
# def obj_init(obj_inst, app_inst):
|
|
||||||
# obj_inst.solid_geometry = mirrored
|
|
||||||
#
|
|
||||||
# self.new_object("gerber", fcobj.options["name"] + "_mirror", obj_init)
|
|
||||||
|
|
||||||
fcobj.mirror(axis, [px, py])
|
fcobj.mirror(axis, [px, py])
|
||||||
fcobj.plot()
|
fcobj.plot()
|
||||||
#self.on_update_plot(None)
|
|
||||||
|
|
||||||
def on_create_aligndrill(self, widget):
|
def on_create_aligndrill(self, widget):
|
||||||
"""
|
"""
|
||||||
@@ -1067,7 +1071,7 @@ class App:
|
|||||||
px, py = eval(self.point_entry.get_text())
|
px, py = eval(self.point_entry.get_text())
|
||||||
else:
|
else:
|
||||||
name = self.box_combo.get_active_text()
|
name = self.box_combo.get_active_text()
|
||||||
bb_obj = self.stuff[name]
|
bb_obj = self.collection.get_by_name(name)
|
||||||
xmin, ymin, xmax, ymax = bb_obj.bounds()
|
xmin, ymin, xmax, ymax = bb_obj.bounds()
|
||||||
px = 0.5*(xmin+xmax)
|
px = 0.5*(xmin+xmax)
|
||||||
py = 0.5*(ymin+ymax)
|
py = 0.5*(ymin+ymax)
|
||||||
@@ -1232,11 +1236,10 @@ class App:
|
|||||||
self.read_form()
|
self.read_form()
|
||||||
scale_options(factor)
|
scale_options(factor)
|
||||||
self.options2form()
|
self.options2form()
|
||||||
for obj in self.stuff:
|
for obj in self.collection.get_list():
|
||||||
units = self.get_radio_value({"rb_mm": "MM", "rb_inch": "IN"})
|
units = self.get_radio_value({"rb_mm": "MM", "rb_inch": "IN"})
|
||||||
#print "Converting ", obj, " to ", units
|
obj.convert_units(units)
|
||||||
self.stuff[obj].convert_units(units)
|
current = self.collection.get_active()
|
||||||
current = self.get_current()
|
|
||||||
if current is not None:
|
if current is not None:
|
||||||
current.to_form()
|
current.to_form()
|
||||||
self.plot_all()
|
self.plot_all()
|
||||||
@@ -1355,7 +1358,7 @@ class App:
|
|||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
obj = self.get_current()
|
obj = self.collection.get_active()
|
||||||
if obj is None:
|
if obj is None:
|
||||||
self.info("WARNING: No object selected.")
|
self.info("WARNING: No object selected.")
|
||||||
return
|
return
|
||||||
@@ -1374,7 +1377,7 @@ class App:
|
|||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
obj = self.get_current()
|
obj = self.collection.get_active()
|
||||||
if obj is None:
|
if obj is None:
|
||||||
self.info("WARNING: No object selected.")
|
self.info("WARNING: No object selected.")
|
||||||
return
|
return
|
||||||
@@ -1393,7 +1396,7 @@ class App:
|
|||||||
:param param: Ignored.
|
:param param: Ignored.
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
obj = self.get_current()
|
obj = self.collection.get_active()
|
||||||
if obj is None:
|
if obj is None:
|
||||||
self.info("WARNING: No object selected.")
|
self.info("WARNING: No object selected.")
|
||||||
return
|
return
|
||||||
@@ -1413,7 +1416,7 @@ class App:
|
|||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
obj = self.get_current()
|
obj = self.collection.get_active()
|
||||||
if obj is None:
|
if obj is None:
|
||||||
self.info("WARNING: No object selected.")
|
self.info("WARNING: No object selected.")
|
||||||
return
|
return
|
||||||
@@ -1501,7 +1504,7 @@ class App:
|
|||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
obj = self.get_current()
|
obj = self.collection.get_active()
|
||||||
factor = self.get_eval("entry_eval_" + obj.kind + "_scalefactor")
|
factor = self.get_eval("entry_eval_" + obj.kind + "_scalefactor")
|
||||||
obj.scale(factor)
|
obj.scale(factor)
|
||||||
obj.to_form()
|
obj.to_form()
|
||||||
@@ -1542,7 +1545,7 @@ class App:
|
|||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
# TODO: Use Gerber.get_bounding_box(...)
|
# TODO: Use Gerber.get_bounding_box(...)
|
||||||
gerber = self.get_current()
|
gerber = self.collection.get_active()
|
||||||
gerber.read_form()
|
gerber.read_form()
|
||||||
name = gerber.options["name"] + "_bbox"
|
name = gerber.options["name"] + "_bbox"
|
||||||
|
|
||||||
@@ -1565,7 +1568,7 @@ class App:
|
|||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
obj = self.get_current()
|
obj = self.collection.get_active()
|
||||||
obj.read_form()
|
obj.read_form()
|
||||||
|
|
||||||
self.set_progress_bar(0.5, "Plotting...")
|
self.set_progress_bar(0.5, "Plotting...")
|
||||||
@@ -1589,7 +1592,7 @@ class App:
|
|||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
excellon = self.get_current()
|
excellon = self.collection.get_active()
|
||||||
excellon.read_form()
|
excellon.read_form()
|
||||||
job_name = excellon.options["name"] + "_cnc"
|
job_name = excellon.options["name"] + "_cnc"
|
||||||
|
|
||||||
@@ -1636,7 +1639,7 @@ class App:
|
|||||||
:param widget: The widget from which this was called.
|
:param widget: The widget from which this was called.
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
excellon = self.get_current()
|
excellon = self.collection.get_active()
|
||||||
assert isinstance(excellon, FlatCAMExcellon)
|
assert isinstance(excellon, FlatCAMExcellon)
|
||||||
excellon.show_tool_chooser()
|
excellon.show_tool_chooser()
|
||||||
|
|
||||||
@@ -1650,7 +1653,7 @@ class App:
|
|||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
self.on_eval_update(widget)
|
self.on_eval_update(widget)
|
||||||
obj = self.get_current()
|
obj = self.collection.get_active()
|
||||||
assert isinstance(obj, FlatCAMObj)
|
assert isinstance(obj, FlatCAMObj)
|
||||||
obj.read_form()
|
obj.read_form()
|
||||||
|
|
||||||
@@ -1664,7 +1667,7 @@ class App:
|
|||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
gerb = self.get_current()
|
gerb = self.collection.get_active()
|
||||||
gerb.read_form()
|
gerb.read_form()
|
||||||
name = gerb.options["name"] + "_noncopper"
|
name = gerb.options["name"] + "_noncopper"
|
||||||
|
|
||||||
@@ -1688,7 +1691,7 @@ class App:
|
|||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
gerb = self.get_current()
|
gerb = self.collection.get_active()
|
||||||
gerb.read_form()
|
gerb.read_form()
|
||||||
name = gerb.options["name"] + "_cutout"
|
name = gerb.options["name"] + "_cutout"
|
||||||
|
|
||||||
@@ -1749,7 +1752,7 @@ class App:
|
|||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
gerb = self.get_current()
|
gerb = self.collection.get_active()
|
||||||
gerb.read_form()
|
gerb.read_form()
|
||||||
dia = gerb.options["isotooldia"]
|
dia = gerb.options["isotooldia"]
|
||||||
passes = int(gerb.options["isopasses"])
|
passes = int(gerb.options["isopasses"])
|
||||||
@@ -1779,7 +1782,7 @@ class App:
|
|||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
source_geo = self.get_current()
|
source_geo = self.collection.get_active()
|
||||||
source_geo.read_form()
|
source_geo.read_form()
|
||||||
job_name = source_geo.options["name"] + "_cnc"
|
job_name = source_geo.options["name"] + "_cnc"
|
||||||
|
|
||||||
@@ -1834,7 +1837,7 @@ class App:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
self.info("Click inside the desired polygon.")
|
self.info("Click inside the desired polygon.")
|
||||||
geo = self.get_current()
|
geo = self.collection.get_active()
|
||||||
geo.read_form()
|
geo.read_form()
|
||||||
assert isinstance(geo, FlatCAMGeometry)
|
assert isinstance(geo, FlatCAMGeometry)
|
||||||
tooldia = geo.options["painttooldia"]
|
tooldia = geo.options["painttooldia"]
|
||||||
@@ -1868,7 +1871,7 @@ class App:
|
|||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
def on_success(app_obj, filename):
|
def on_success(app_obj, filename):
|
||||||
cncjob = app_obj.get_current()
|
cncjob = app_obj.collection.get_active()
|
||||||
f = open(filename, 'w')
|
f = open(filename, 'w')
|
||||||
f.write(cncjob.gcode)
|
f.write(cncjob.gcode)
|
||||||
f.close()
|
f.close()
|
||||||
@@ -1885,17 +1888,17 @@ class App:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# Keep this for later
|
# Keep this for later
|
||||||
name = copy.copy(self.selected_item_name)
|
name = copy.copy(self.collection.get_active().options["name"])
|
||||||
|
|
||||||
# Remove plot
|
# Remove plot
|
||||||
self.plotcanvas.figure.delaxes(self.get_current().axes)
|
self.plotcanvas.figure.delaxes(self.collection.get_active().axes)
|
||||||
self.plotcanvas.auto_adjust_axes()
|
self.plotcanvas.auto_adjust_axes()
|
||||||
|
|
||||||
# Remove from dictionary
|
# Clear form
|
||||||
self.stuff.pop(self.selected_item_name)
|
self.setup_component_editor()
|
||||||
|
|
||||||
# Update UI
|
# Remove from dictionary
|
||||||
self.build_list() # Update the items list
|
self.collection.delete_active()
|
||||||
|
|
||||||
self.info("Object deleted: %s" % name)
|
self.info("Object deleted: %s" % name)
|
||||||
|
|
||||||
@@ -1907,7 +1910,7 @@ class App:
|
|||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.get_current().read_form()
|
self.collection.get_active().read_form()
|
||||||
|
|
||||||
self.plot_all()
|
self.plot_all()
|
||||||
|
|
||||||
@@ -1929,49 +1932,10 @@ class App:
|
|||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Disconnect event listener
|
old_name = copy.copy(self.collection.get_active().options["name"])
|
||||||
self.tree.get_selection().disconnect(self.signal_id)
|
new_name = entry.get_text()
|
||||||
|
self.collection.change_name(old_name, new_name)
|
||||||
new_name = entry.get_text() # Get from form
|
self.info("Name changed from %s to %s" % (old_name, new_name))
|
||||||
self.stuff[new_name] = self.stuff.pop(self.selected_item_name) # Update dictionary
|
|
||||||
self.stuff[new_name].options["name"] = new_name # update object
|
|
||||||
self.info('Name change: ' + self.selected_item_name + " to " + new_name)
|
|
||||||
|
|
||||||
self.selected_item_name = new_name # Update selection name
|
|
||||||
|
|
||||||
self.build_list() # Update the items list
|
|
||||||
|
|
||||||
# Reconnect event listener
|
|
||||||
self.signal_id = self.tree.get_selection().connect(
|
|
||||||
"changed", self.on_tree_selection_changed)
|
|
||||||
|
|
||||||
def on_tree_selection_changed(self, selection):
|
|
||||||
"""
|
|
||||||
Callback for selection change in the project list. This changes
|
|
||||||
the currently selected FlatCAMObj.
|
|
||||||
|
|
||||||
:param selection: Selection associated to the project tree or list
|
|
||||||
:type selection: Gtk.TreeSelection
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
print "DEBUG: on_tree_selection_change(): ",
|
|
||||||
model, treeiter = selection.get_selected()
|
|
||||||
|
|
||||||
if treeiter is not None:
|
|
||||||
# Save data for previous selection
|
|
||||||
obj = self.get_current()
|
|
||||||
if obj is not None:
|
|
||||||
obj.read_form()
|
|
||||||
|
|
||||||
print "DEBUG: You selected", model[treeiter][0]
|
|
||||||
self.selected_item_name = model[treeiter][0]
|
|
||||||
obj_new = self.get_current()
|
|
||||||
if obj_new is not None:
|
|
||||||
GLib.idle_add(lambda: obj_new.build_ui())
|
|
||||||
else:
|
|
||||||
print "DEBUG: Nothing selected"
|
|
||||||
self.selected_item_name = None
|
|
||||||
self.setup_component_editor()
|
|
||||||
|
|
||||||
def on_file_new(self, param):
|
def on_file_new(self, param):
|
||||||
"""
|
"""
|
||||||
@@ -1981,19 +1945,18 @@ class App:
|
|||||||
:param param: Whatever is passed by the event. Ignore.
|
:param param: Whatever is passed by the event. Ignore.
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
# Remove everythong from memory
|
# Remove everything from memory
|
||||||
# Clear plot
|
# Clear plot
|
||||||
self.plotcanvas.clear()
|
self.plotcanvas.clear()
|
||||||
|
|
||||||
# Clear object editor
|
# Delete data
|
||||||
#self.setup_component_editor()
|
self.collection.delete_all()
|
||||||
|
|
||||||
# Clear data
|
# Clear object editor
|
||||||
self.stuff = {}
|
self.setup_component_editor()
|
||||||
|
|
||||||
# Clear list
|
# Clear list
|
||||||
#self.tree_select.unselect_all()
|
self.collection.build_list()
|
||||||
self.build_list()
|
|
||||||
|
|
||||||
# Clear project filename
|
# Clear project filename
|
||||||
self.project_filename = None
|
self.project_filename = None
|
||||||
@@ -2339,7 +2302,7 @@ class App:
|
|||||||
:param event: Ignored.
|
:param event: Ignored.
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
xmin, ymin, xmax, ymax = get_bounds(self.stuff)
|
xmin, ymin, xmax, ymax = self.collection.get_bounds()
|
||||||
width = xmax - xmin
|
width = xmax - xmin
|
||||||
height = ymax - ymin
|
height = ymax - ymin
|
||||||
xmin -= 0.05 * width
|
xmin -= 0.05 * width
|
||||||
@@ -2804,11 +2767,79 @@ class PlotCanvas:
|
|||||||
|
|
||||||
|
|
||||||
class ObjectCollection:
|
class ObjectCollection:
|
||||||
|
|
||||||
|
classdict = {
|
||||||
|
"gerber": FlatCAMGerber,
|
||||||
|
"excellon": FlatCAMExcellon,
|
||||||
|
"cncjob": FlatCAMCNCjob,
|
||||||
|
"geometry": FlatCAMGeometry
|
||||||
|
}
|
||||||
|
|
||||||
|
icons = {
|
||||||
|
"gerber": "share/flatcam_icon16.png",
|
||||||
|
"excellon": "share/drill16.png",
|
||||||
|
"cncjob": "share/cnc16.png",
|
||||||
|
"geometry": "share/geometry16.png"
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
|
### Data
|
||||||
|
# List of FLatCAMObject's
|
||||||
self.collection = []
|
self.collection = []
|
||||||
self.active = None
|
self.active = None
|
||||||
|
|
||||||
|
### GUI List components
|
||||||
|
## Model
|
||||||
|
self.store = Gtk.ListStore(GdkPixbuf.Pixbuf, str)
|
||||||
|
|
||||||
|
## 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
|
||||||
|
renderer_pixbuf = Gtk.CellRendererPixbuf()
|
||||||
|
column_pixbuf = Gtk.TreeViewColumn("Type", renderer_pixbuf, pixbuf=0)
|
||||||
|
self.view.append_column(column_pixbuf)
|
||||||
|
|
||||||
|
renderer_text = Gtk.CellRendererText()
|
||||||
|
column_text = Gtk.TreeViewColumn("Name", renderer_text, text=1)
|
||||||
|
self.view.append_column(column_text)
|
||||||
|
|
||||||
|
def delete_all(self):
|
||||||
|
print "OC.delete_all()"
|
||||||
|
self.collection = []
|
||||||
|
self.active = None
|
||||||
|
|
||||||
|
def delete_active(self):
|
||||||
|
print "OC.delete_active()"
|
||||||
|
self.collection.remove(self.active)
|
||||||
|
self.active = None
|
||||||
|
self.build_list()
|
||||||
|
|
||||||
|
def on_row_activated(self, *args):
|
||||||
|
print "OC.on_row_activated()"
|
||||||
|
return
|
||||||
|
|
||||||
|
def on_list_selection_change(self, selection):
|
||||||
|
print "OC.on_list_selection_change()"
|
||||||
|
model, treeiter = selection.get_selected()
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.get_active().read_form()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.set_active(model[treeiter][1])
|
||||||
|
self.get_active().build_ui()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
def set_active(self, name):
|
def set_active(self, name):
|
||||||
|
print "OC.set_active()"
|
||||||
for obj in self.collection:
|
for obj in self.collection:
|
||||||
if obj.options['name'] == name:
|
if obj.options['name'] == name:
|
||||||
self.active = obj
|
self.active = obj
|
||||||
@@ -2816,10 +2847,63 @@ class ObjectCollection:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def get_active(self):
|
def get_active(self):
|
||||||
|
print "OC.get_active()"
|
||||||
return self.active
|
return self.active
|
||||||
|
|
||||||
def append(self, obj):
|
def set_list_selection(self, name):
|
||||||
self.collection.append(obj)
|
print "OC.set_list_selection()"
|
||||||
|
iterat = self.store.get_iter_first()
|
||||||
|
while iterat is not None and self.store[iterat][1] != name:
|
||||||
|
iterat = self.store.iter_next(iterat)
|
||||||
|
self.tree_selection.unselect_all()
|
||||||
|
self.tree_selection.select_iter(iterat)
|
||||||
|
|
||||||
|
def append(self, obj, active=False):
|
||||||
|
print "OC.append()"
|
||||||
|
if obj not in self.collection:
|
||||||
|
self.collection.append(obj)
|
||||||
|
self.build_list()
|
||||||
|
|
||||||
|
if active:
|
||||||
|
self.set_list_selection(obj.options["name"])
|
||||||
|
|
||||||
|
def get_names(self):
|
||||||
|
print "OC.get_names()"
|
||||||
|
return [o.options["name"] for o in self.collection]
|
||||||
|
|
||||||
|
def build_list(self):
|
||||||
|
print "OC.build_list()"
|
||||||
|
self.store.clear()
|
||||||
|
for obj in self.collection:
|
||||||
|
icon = GdkPixbuf.Pixbuf.new_from_file(ObjectCollection.icons[obj.kind])
|
||||||
|
self.store.append([icon, obj.options["name"]])
|
||||||
|
|
||||||
|
def get_bounds(self):
|
||||||
|
print "OC.get_bounds()"
|
||||||
|
return get_bounds(self.collection)
|
||||||
|
|
||||||
|
def get_list(self):
|
||||||
|
return self.collection
|
||||||
|
|
||||||
|
def get_by_name(self, name):
|
||||||
|
for obj in self.collection:
|
||||||
|
if obj.options["name"] == name:
|
||||||
|
return obj
|
||||||
|
return None
|
||||||
|
|
||||||
|
def change_name(self, old_name, new_name):
|
||||||
|
self.tree_selection.disconnect(self.change_subscription)
|
||||||
|
|
||||||
|
for obj in self.collection:
|
||||||
|
if obj.options["name"] == old_name:
|
||||||
|
obj.options["name"] = new_name
|
||||||
|
self.build_list()
|
||||||
|
self.tree_selection.connect("changed", self.on_list_selection_change)
|
||||||
|
if obj == self.get_active():
|
||||||
|
self.set_active(new_name)
|
||||||
|
self.set_list_selection(new_name)
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
app = App()
|
app = App()
|
||||||
Gtk.main()
|
Gtk.main()
|
||||||
|
|||||||
797
FlatCAM.ui
797
FlatCAM.ui
@@ -132,6 +132,404 @@ THE SOFTWARE.</property>
|
|||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="stock">gtk-open</property>
|
<property name="stock">gtk-open</property>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="GtkOffscreenWindow" id="offscreenwindow_dblsided">
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkScrolledWindow" id="sw_dblsided">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="hscrollbar_policy">never</property>
|
||||||
|
<property name="shadow_type">in</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkViewport" id="vp_dblsided">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="box_dblsided">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="margin_left">5</property>
|
||||||
|
<property name="margin_right">5</property>
|
||||||
|
<property name="margin_top">5</property>
|
||||||
|
<property name="margin_bottom">5</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label53">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="margin_bottom">6</property>
|
||||||
|
<property name="ypad">3</property>
|
||||||
|
<property name="label" translatable="yes">Double-Sided PCB Tool</property>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="weight" value="semibold"/>
|
||||||
|
</attributes>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkGrid" id="grid7">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="row_spacing">3</property>
|
||||||
|
<property name="column_spacing">3</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label84">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_markup" translatable="yes">The object that you want to flip around,
|
||||||
|
usually the Gerber object defining the
|
||||||
|
bottom copper layer. You can also flip
|
||||||
|
an Excellon object in case you want to drill
|
||||||
|
from the bottom side.</property>
|
||||||
|
<property name="xalign">1</property>
|
||||||
|
<property name="xpad">3</property>
|
||||||
|
<property name="label" translatable="yes">Bottom Layer:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
<property name="width">1</property>
|
||||||
|
<property name="height">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkComboBoxText" id="comboboxtext_bottomlayer">
|
||||||
|
<property name="width_request">200</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="entry_text_column">0</property>
|
||||||
|
<property name="id_column">1</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
<property name="width">1</property>
|
||||||
|
<property name="height">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label85">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_markup" translatable="yes"><b>X</b> flips from top to bottom,
|
||||||
|
<b>Y</b> flips from left to right.</property>
|
||||||
|
<property name="xalign">1</property>
|
||||||
|
<property name="xpad">3</property>
|
||||||
|
<property name="label" translatable="yes">Mirror Axis:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
<property name="width">1</property>
|
||||||
|
<property name="height">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="box24">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="spacing">10</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkRadioButton" id="rb_mirror_x">
|
||||||
|
<property name="label" translatable="yes">X</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="active">True</property>
|
||||||
|
<property name="draw_indicator">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkRadioButton" id="rb_mirror_y">
|
||||||
|
<property name="label" translatable="yes">Y</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="draw_indicator">True</property>
|
||||||
|
<property name="group">rb_mirror_x</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
<property name="width">1</property>
|
||||||
|
<property name="height">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label86">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_markup" translatable="yes">How the location of the axis
|
||||||
|
is specified.</property>
|
||||||
|
<property name="xalign">1</property>
|
||||||
|
<property name="xpad">3</property>
|
||||||
|
<property name="label" translatable="yes">Axis location:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">2</property>
|
||||||
|
<property name="width">1</property>
|
||||||
|
<property name="height">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="box25">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="spacing">10</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkRadioButton" id="rb_mirror_point">
|
||||||
|
<property name="label" translatable="yes">Point</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">False</property>
|
||||||
|
<property name="tooltip_markup" translatable="yes">The axis must pass through the
|
||||||
|
specified point.</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="active">True</property>
|
||||||
|
<property name="draw_indicator">True</property>
|
||||||
|
<signal name="toggled" handler="on_toggle_pointbox" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkRadioButton" id="rb_mirror_box">
|
||||||
|
<property name="label" translatable="yes">Box</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">False</property>
|
||||||
|
<property name="tooltip_markup" translatable="yes">The axis cuts a box (some Geometry object
|
||||||
|
in the project) exactly in the middle.</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="draw_indicator">True</property>
|
||||||
|
<property name="group">rb_mirror_point</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">2</property>
|
||||||
|
<property name="width">1</property>
|
||||||
|
<property name="height">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label87">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_markup" translatable="yes"><b>Point:</b> Click on the desired point on the plot.
|
||||||
|
This copies the point to the clipboard. Then paste it
|
||||||
|
in the box by right-clicking and choosing paste, or
|
||||||
|
hitting Control-v.
|
||||||
|
|
||||||
|
<b>Box:</b> Choose an object in the project
|
||||||
|
that you want to use as a box for specifying
|
||||||
|
the flipping axis. If the object is not a rectangle,
|
||||||
|
a bounding box arounf the object is calculated.</property>
|
||||||
|
<property name="xalign">1</property>
|
||||||
|
<property name="xpad">3</property>
|
||||||
|
<property name="label" translatable="yes">Point/Box:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">3</property>
|
||||||
|
<property name="width">1</property>
|
||||||
|
<property name="height">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="box_pointbox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">3</property>
|
||||||
|
<property name="width">1</property>
|
||||||
|
<property name="height">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label89">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_markup" translatable="yes">List of coordinates where to drill alignment
|
||||||
|
holes, in the format <b>(x1, y1), (x2, y2)</b>, etc.
|
||||||
|
You can click on the plot and paste each
|
||||||
|
coordinate here.
|
||||||
|
|
||||||
|
All <b>coordinates are duplicated</b> and mirrored
|
||||||
|
automatically around the axis so drill pattens are
|
||||||
|
identical when flipping your board around.</property>
|
||||||
|
<property name="xalign">1</property>
|
||||||
|
<property name="xpad">3</property>
|
||||||
|
<property name="label" translatable="yes">Algnmt holes:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">4</property>
|
||||||
|
<property name="width">1</property>
|
||||||
|
<property name="height">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="entry_dblsided_alignholes">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="invisible_char">●</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">4</property>
|
||||||
|
<property name="width">1</property>
|
||||||
|
<property name="height">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label90">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_markup" translatable="yes">Diameter of the drill for
|
||||||
|
the aligment holes.</property>
|
||||||
|
<property name="xalign">1</property>
|
||||||
|
<property name="xpad">3</property>
|
||||||
|
<property name="label" translatable="yes">Drill diam.:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">5</property>
|
||||||
|
<property name="width">1</property>
|
||||||
|
<property name="height">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="entry_dblsided_alignholediam">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="invisible_char">●</property>
|
||||||
|
<property name="invisible_char_set">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">5</property>
|
||||||
|
<property name="width">1</property>
|
||||||
|
<property name="height">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="box27">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">end</property>
|
||||||
|
<property name="margin_top">6</property>
|
||||||
|
<property name="margin_bottom">3</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="button19">
|
||||||
|
<property name="label" translatable="yes">Create Alignment Drill</property>
|
||||||
|
<property name="width_request">120</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="tooltip_markup" translatable="yes">Creates an Excellon object with
|
||||||
|
the specified holes and their
|
||||||
|
mirror pairs.</property>
|
||||||
|
<property name="halign">end</property>
|
||||||
|
<signal name="activate" handler="on_create_aligndrill" swapped="no"/>
|
||||||
|
<signal name="clicked" handler="on_create_aligndrill" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="button18">
|
||||||
|
<property name="label" translatable="yes">Mirror Object</property>
|
||||||
|
<property name="width_request">120</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="tooltip_markup" translatable="yes">Mirrors the object specified in
|
||||||
|
<b>Bottom Layer</b> around the
|
||||||
|
specified axis.</property>
|
||||||
|
<property name="halign">end</property>
|
||||||
|
<signal name="activate" handler="on_create_mirror" swapped="no"/>
|
||||||
|
<signal name="clicked" handler="on_create_mirror" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
<object class="GtkOffscreenWindow" id="offscrwindow_cncjob">
|
<object class="GtkOffscreenWindow" id="offscrwindow_cncjob">
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<child>
|
<child>
|
||||||
@@ -1145,7 +1543,6 @@ after hitting Enter in the text box.</property>
|
|||||||
<property name="can_focus">True</property>
|
<property name="can_focus">True</property>
|
||||||
<property name="invisible_char">●</property>
|
<property name="invisible_char">●</property>
|
||||||
<property name="invisible_char_set">True</property>
|
<property name="invisible_char_set">True</property>
|
||||||
<signal name="activate" handler="on_activate_name" swapped="no"/>
|
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="expand">False</property>
|
||||||
@@ -2721,404 +3118,6 @@ this object.</property>
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<object class="GtkOffscreenWindow" id="offscreenwindow_dblsided">
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkScrolledWindow" id="sw_dblsided">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="hscrollbar_policy">never</property>
|
|
||||||
<property name="shadow_type">in</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkViewport" id="vp_dblsided">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox" id="box_dblsided">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="margin_left">5</property>
|
|
||||||
<property name="margin_right">5</property>
|
|
||||||
<property name="margin_top">5</property>
|
|
||||||
<property name="margin_bottom">5</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="label53">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="margin_bottom">6</property>
|
|
||||||
<property name="ypad">3</property>
|
|
||||||
<property name="label" translatable="yes">Double-Sided PCB Tool</property>
|
|
||||||
<attributes>
|
|
||||||
<attribute name="weight" value="semibold"/>
|
|
||||||
</attributes>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkGrid" id="grid7">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="row_spacing">3</property>
|
|
||||||
<property name="column_spacing">3</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="label84">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="tooltip_markup" translatable="yes">The object that you want to flip around,
|
|
||||||
usually the Gerber object defining the
|
|
||||||
bottom copper layer. You can also flip
|
|
||||||
an Excellon object in case you want to drill
|
|
||||||
from the bottom side.</property>
|
|
||||||
<property name="xalign">1</property>
|
|
||||||
<property name="xpad">3</property>
|
|
||||||
<property name="label" translatable="yes">Bottom Layer:</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">0</property>
|
|
||||||
<property name="top_attach">0</property>
|
|
||||||
<property name="width">1</property>
|
|
||||||
<property name="height">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkComboBoxText" id="comboboxtext_bottomlayer">
|
|
||||||
<property name="width_request">200</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="halign">start</property>
|
|
||||||
<property name="entry_text_column">0</property>
|
|
||||||
<property name="id_column">1</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">1</property>
|
|
||||||
<property name="top_attach">0</property>
|
|
||||||
<property name="width">1</property>
|
|
||||||
<property name="height">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="label85">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="tooltip_markup" translatable="yes"><b>X</b> flips from top to bottom,
|
|
||||||
<b>Y</b> flips from left to right.</property>
|
|
||||||
<property name="xalign">1</property>
|
|
||||||
<property name="xpad">3</property>
|
|
||||||
<property name="label" translatable="yes">Mirror Axis:</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">0</property>
|
|
||||||
<property name="top_attach">1</property>
|
|
||||||
<property name="width">1</property>
|
|
||||||
<property name="height">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox" id="box24">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="spacing">10</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkRadioButton" id="rb_mirror_x">
|
|
||||||
<property name="label" translatable="yes">X</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="receives_default">False</property>
|
|
||||||
<property name="xalign">0</property>
|
|
||||||
<property name="active">True</property>
|
|
||||||
<property name="draw_indicator">True</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkRadioButton" id="rb_mirror_y">
|
|
||||||
<property name="label" translatable="yes">Y</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="receives_default">False</property>
|
|
||||||
<property name="xalign">0</property>
|
|
||||||
<property name="draw_indicator">True</property>
|
|
||||||
<property name="group">rb_mirror_x</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">1</property>
|
|
||||||
<property name="top_attach">1</property>
|
|
||||||
<property name="width">1</property>
|
|
||||||
<property name="height">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="label86">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="tooltip_markup" translatable="yes">How the location of the axis
|
|
||||||
is specified.</property>
|
|
||||||
<property name="xalign">1</property>
|
|
||||||
<property name="xpad">3</property>
|
|
||||||
<property name="label" translatable="yes">Axis location:</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">0</property>
|
|
||||||
<property name="top_attach">2</property>
|
|
||||||
<property name="width">1</property>
|
|
||||||
<property name="height">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox" id="box25">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="spacing">10</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkRadioButton" id="rb_mirror_point">
|
|
||||||
<property name="label" translatable="yes">Point</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="receives_default">False</property>
|
|
||||||
<property name="tooltip_markup" translatable="yes">The axis must pass through the
|
|
||||||
specified point.</property>
|
|
||||||
<property name="xalign">0</property>
|
|
||||||
<property name="active">True</property>
|
|
||||||
<property name="draw_indicator">True</property>
|
|
||||||
<signal name="toggled" handler="on_toggle_pointbox" swapped="no"/>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkRadioButton" id="rb_mirror_box">
|
|
||||||
<property name="label" translatable="yes">Box</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="receives_default">False</property>
|
|
||||||
<property name="tooltip_markup" translatable="yes">The axis cuts a box (some Geometry object
|
|
||||||
in the project) exactly in the middle.</property>
|
|
||||||
<property name="xalign">0</property>
|
|
||||||
<property name="draw_indicator">True</property>
|
|
||||||
<property name="group">rb_mirror_point</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">1</property>
|
|
||||||
<property name="top_attach">2</property>
|
|
||||||
<property name="width">1</property>
|
|
||||||
<property name="height">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="label87">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="tooltip_markup" translatable="yes"><b>Point:</b> Click on the desired point on the plot.
|
|
||||||
This copies the point to the clipboard. Then paste it
|
|
||||||
in the box by right-clicking and choosing paste, or
|
|
||||||
hitting Control-v.
|
|
||||||
|
|
||||||
<b>Box:</b> Choose an object in the project
|
|
||||||
that you want to use as a box for specifying
|
|
||||||
the flipping axis. If the object is not a rectangle,
|
|
||||||
a bounding box arounf the object is calculated.</property>
|
|
||||||
<property name="xalign">1</property>
|
|
||||||
<property name="xpad">3</property>
|
|
||||||
<property name="label" translatable="yes">Point/Box:</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">0</property>
|
|
||||||
<property name="top_attach">3</property>
|
|
||||||
<property name="width">1</property>
|
|
||||||
<property name="height">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox" id="box_pointbox">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<child>
|
|
||||||
<placeholder/>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">1</property>
|
|
||||||
<property name="top_attach">3</property>
|
|
||||||
<property name="width">1</property>
|
|
||||||
<property name="height">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="label89">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="tooltip_markup" translatable="yes">List of coordinates where to drill alignment
|
|
||||||
holes, in the format <b>(x1, y1), (x2, y2)</b>, etc.
|
|
||||||
You can click on the plot and paste each
|
|
||||||
coordinate here.
|
|
||||||
|
|
||||||
All <b>coordinates are duplicated</b> and mirrored
|
|
||||||
automatically around the axis so drill pattens are
|
|
||||||
identical when flipping your board around.</property>
|
|
||||||
<property name="xalign">1</property>
|
|
||||||
<property name="xpad">3</property>
|
|
||||||
<property name="label" translatable="yes">Algnmt holes:</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">0</property>
|
|
||||||
<property name="top_attach">4</property>
|
|
||||||
<property name="width">1</property>
|
|
||||||
<property name="height">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkEntry" id="entry_dblsided_alignholes">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="invisible_char">●</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">1</property>
|
|
||||||
<property name="top_attach">4</property>
|
|
||||||
<property name="width">1</property>
|
|
||||||
<property name="height">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="label90">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="tooltip_markup" translatable="yes">Diameter of the drill for
|
|
||||||
the aligment holes.</property>
|
|
||||||
<property name="xalign">1</property>
|
|
||||||
<property name="xpad">3</property>
|
|
||||||
<property name="label" translatable="yes">Drill diam.:</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">0</property>
|
|
||||||
<property name="top_attach">5</property>
|
|
||||||
<property name="width">1</property>
|
|
||||||
<property name="height">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkEntry" id="entry_dblsided_alignholediam">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="invisible_char">●</property>
|
|
||||||
<property name="invisible_char_set">True</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">1</property>
|
|
||||||
<property name="top_attach">5</property>
|
|
||||||
<property name="width">1</property>
|
|
||||||
<property name="height">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox" id="box27">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="halign">end</property>
|
|
||||||
<property name="margin_top">6</property>
|
|
||||||
<property name="margin_bottom">3</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkButton" id="button19">
|
|
||||||
<property name="label" translatable="yes">Create Alignment Drill</property>
|
|
||||||
<property name="width_request">120</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="receives_default">True</property>
|
|
||||||
<property name="tooltip_markup" translatable="yes">Creates an Excellon object with
|
|
||||||
the specified holes and their
|
|
||||||
mirror pairs.</property>
|
|
||||||
<property name="halign">end</property>
|
|
||||||
<signal name="activate" handler="on_create_aligndrill" swapped="no"/>
|
|
||||||
<signal name="clicked" handler="on_create_aligndrill" swapped="no"/>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">False</property>
|
|
||||||
<property name="padding">4</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkButton" id="button18">
|
|
||||||
<property name="label" translatable="yes">Mirror Object</property>
|
|
||||||
<property name="width_request">120</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="receives_default">True</property>
|
|
||||||
<property name="tooltip_markup" translatable="yes">Mirrors the object specified in
|
|
||||||
<b>Bottom Layer</b> around the
|
|
||||||
specified axis.</property>
|
|
||||||
<property name="halign">end</property>
|
|
||||||
<signal name="activate" handler="on_create_mirror" swapped="no"/>
|
|
||||||
<signal name="clicked" handler="on_create_mirror" swapped="no"/>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">False</property>
|
|
||||||
<property name="padding">4</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">2</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<placeholder/>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<placeholder/>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<placeholder/>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<placeholder/>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<object class="GtkWindow" id="window1">
|
<object class="GtkWindow" id="window1">
|
||||||
<property name="width_request">600</property>
|
<property name="width_request">600</property>
|
||||||
<property name="height_request">400</property>
|
<property name="height_request">400</property>
|
||||||
|
|||||||
@@ -111,8 +111,8 @@ class FlatCAMObj:
|
|||||||
# Put in the UI
|
# Put in the UI
|
||||||
box_selected.pack_start(sw, True, True, 0)
|
box_selected.pack_start(sw, True, True, 0)
|
||||||
|
|
||||||
entry_name = self.app.builder.get_object("entry_text_" + self.kind + "_name")
|
# entry_name = self.app.builder.get_object("entry_text_" + self.kind + "_name")
|
||||||
entry_name.connect("activate", self.app.on_activate_name)
|
# entry_name.connect("activate", self.app.on_activate_name)
|
||||||
self.to_form()
|
self.to_form()
|
||||||
sw.show()
|
sw.show()
|
||||||
|
|
||||||
|
|||||||
26
camlib.py
26
camlib.py
@@ -2251,16 +2251,35 @@ class CNCjob(Geometry):
|
|||||||
self.create_geometry()
|
self.create_geometry()
|
||||||
|
|
||||||
|
|
||||||
def get_bounds(geometry_set):
|
# def get_bounds(geometry_set):
|
||||||
|
# xmin = Inf
|
||||||
|
# ymin = Inf
|
||||||
|
# xmax = -Inf
|
||||||
|
# ymax = -Inf
|
||||||
|
#
|
||||||
|
# #print "Getting bounds of:", str(geometry_set)
|
||||||
|
# for gs in geometry_set:
|
||||||
|
# try:
|
||||||
|
# gxmin, gymin, gxmax, gymax = geometry_set[gs].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."
|
||||||
|
#
|
||||||
|
# return [xmin, ymin, xmax, ymax]
|
||||||
|
|
||||||
|
def get_bounds(geometry_list):
|
||||||
xmin = Inf
|
xmin = Inf
|
||||||
ymin = Inf
|
ymin = Inf
|
||||||
xmax = -Inf
|
xmax = -Inf
|
||||||
ymax = -Inf
|
ymax = -Inf
|
||||||
|
|
||||||
#print "Getting bounds of:", str(geometry_set)
|
#print "Getting bounds of:", str(geometry_set)
|
||||||
for gs in geometry_set:
|
for gs in geometry_list:
|
||||||
try:
|
try:
|
||||||
gxmin, gymin, gxmax, gymax = geometry_set[gs].bounds()
|
gxmin, gymin, gxmax, gymax = gs.bounds()
|
||||||
xmin = min([xmin, gxmin])
|
xmin = min([xmin, gxmin])
|
||||||
ymin = min([ymin, gymin])
|
ymin = min([ymin, gymin])
|
||||||
xmax = max([xmax, gxmax])
|
xmax = max([xmax, gxmax])
|
||||||
@@ -2270,7 +2289,6 @@ def get_bounds(geometry_set):
|
|||||||
|
|
||||||
return [xmin, ymin, xmax, ymax]
|
return [xmin, ymin, xmax, ymax]
|
||||||
|
|
||||||
|
|
||||||
def arc(center, radius, start, stop, direction, steps_per_circ):
|
def arc(center, radius, start, stop, direction, steps_per_circ):
|
||||||
"""
|
"""
|
||||||
Creates a list of point along the specified arc.
|
Creates a list of point along the specified arc.
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
[{"kind": "excellon", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\BLDC2003Through.drl"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\PhD\\PLLs\\RTWO\\Project Outputs for RTWO1\\PCB1.GTL"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\BLDC_1303_Bottom.gbr"}, {"kind": "excellon", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\PlacaReles.drl"}, {"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\\Example1_copper_bottom.gbr"}, {"kind": "cncjob", "filename": "C:\\Users\\jpcaram\\Dropbox\\PhD\\PLLs\\RTWO\\RTWO1_CNC\\cutout1.gcode"}, {"kind": "project", "filename": "C:\\Users\\jpcaram\\Dropbox\\PhD\\PLLs\\RTWO\\RTWO1.fcproj"}, {"kind": "excellon", "filename": "C:\\Users\\jpcaram\\Dropbox\\PhD\\PLLs\\RTWO\\Project Outputs for RTWO1\\PCB1.TXT"}, {"kind": "excellon", "filename": "C:\\Users\\jpcaram\\Dropbox\\PhD\\PLLs\\RTWO\\Project Outputs for RTWO1\\PCB1.DRL"}]
|
[{"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\PhD\\PLLs\\RTWO\\Project Outputs for RTWO1\\PCB1.GBL"}, {"kind": "excellon", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\BLDC2003Through.drl"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\PhD\\PLLs\\RTWO\\Project Outputs for RTWO1\\PCB1.GTL"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\BLDC_1303_Bottom.gbr"}, {"kind": "excellon", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\PlacaReles.drl"}, {"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\\Example1_copper_bottom.gbr"}, {"kind": "cncjob", "filename": "C:\\Users\\jpcaram\\Dropbox\\PhD\\PLLs\\RTWO\\RTWO1_CNC\\cutout1.gcode"}, {"kind": "project", "filename": "C:\\Users\\jpcaram\\Dropbox\\PhD\\PLLs\\RTWO\\RTWO1.fcproj"}, {"kind": "excellon", "filename": "C:\\Users\\jpcaram\\Dropbox\\PhD\\PLLs\\RTWO\\Project Outputs for RTWO1\\PCB1.TXT"}]
|
||||||
Reference in New Issue
Block a user