diff --git a/camlib.py b/camlib.py
index f6f8e3cf..88681e70 100644
--- a/camlib.py
+++ b/camlib.py
@@ -1,12 +1,3 @@
-#import cairo
-
-#from string import *
-#from math import *
-#from random import *
-#from struct import *
-#import os
-#import sys
-
from numpy import arctan2, Inf, array, sqrt, pi, ceil, sin, cos
from matplotlib.figure import Figure
@@ -346,7 +337,7 @@ class Excellon(Geometry):
class CNCjob(Geometry):
def __init__(self, units="in", kind="generic", z_move = 0.1,
- feedrate = 3.0, z_cut = -0.002):
+ feedrate = 3.0, z_cut = -0.002, tooldia = 0.0):
# Options
self.kind = kind
@@ -354,6 +345,7 @@ class CNCjob(Geometry):
self.z_cut = z_cut
self.z_move = z_move
self.feedrate = feedrate
+ self.tooldia = tooldia
# Constants
self.unitcode = {"in": "G20", "mm": "G21"}
@@ -367,8 +359,7 @@ class CNCjob(Geometry):
# Bounds of geometry given to CNCjob.generate_from_geometry()
self.input_geometry_bounds = None
- # Tool diameter given to CNCjob.generate_from_geometry()
- self.tooldia = 0
+
# Output generated by CNCjob.create_gcode_geometry()
#self.G_geometry = None
diff --git a/cirkuix.py b/cirkuix.py
index 2e48a162..154f3159 100644
--- a/cirkuix.py
+++ b/cirkuix.py
@@ -1,35 +1,35 @@
-from gi.repository import Gtk
-import matplotlib.pyplot as plt
-plt.ioff()
+import threading
+from gi.repository import Gtk
from matplotlib.figure import Figure
from numpy import arange, sin, pi
from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas
-#from matplotlib.backends.backend_gtk3 import NavigationToolbar2GTK3 as NavigationToolbar
#from matplotlib.backends.backend_gtk3cairo import FigureCanvasGTK3Cairo as FigureCanvas
#from matplotlib.backends.backend_cairo import FigureCanvasCairo as FigureCanvas
-#import cairo
from camlib import *
+
class CirkuixObj:
def __init__(self, name, kind):
self.name = name
self.kind = kind
-
+
+
class CirkuixGerber(CirkuixObj, Gerber):
def __init__(self, name):
Gerber.__init__(self)
CirkuixObj.__init__(self, name, "gerber")
- self.fields = [{"name":"plot",
- "type":bool,
- "value":True,
- "get":None,
- "set":None,
- "onchange":None},
+ self.fields = [{"name": "plot",
+ "type": bool,
+ "value": True,
+ "get": None,
+ "set": None,
+ "onchange": None},
{}]
-
+
+
class CirkuixExcellon(CirkuixObj, Excellon):
def __init__(self, name):
Excellon.__init__(self)
@@ -37,13 +37,17 @@ class CirkuixExcellon(CirkuixObj, Excellon):
self.options = {"plot": True,
"solid": False,
"multicolored": False}
-
+
+
class CirkuixCNCjob(CirkuixObj, CNCjob):
- def __init__(self, name):
- CNCjob.__init__(self)
+ def __init__(self, name, units="in", kind="generic", z_move=0.1,
+ feedrate=3.0, z_cut=-0.002, tooldia=0.0):
+ CNCjob.__init__(self, units=units, kind=kind, z_move=z_move,
+ feedrate=feedrate, z_cut=z_cut, tooldia=tooldia)
CirkuixObj.__init__(self, name, "cncjob")
self.options = {"plot": True}
+
class CirkuixGeometry(CirkuixObj, Geometry):
def __init__(self, name):
CirkuixObj.__init__(self, name, "geometry")
@@ -51,9 +55,10 @@ class CirkuixGeometry(CirkuixObj, Geometry):
"solid": False,
"multicolored": False}
+
class CirkuixObjForm:
- def __init__(self, container, Cobj):
- self.Cobj = Cobj
+ def __init__(self, container, cobj):
+ self.cobj = cobj
self.container = container
self.fields = {}
@@ -68,13 +73,16 @@ class CirkuixObjForm:
def get_entry_text(entry):
return entry.get_text()
-
+
+
def get_entry_int(entry):
return int(entry.get_text())
-
+
+
def get_entry_float(entry):
return float(entry.get_text())
-
+
+
def get_entry_eval(entry):
return eval(entry.get_text)
@@ -85,6 +93,7 @@ getters = {"entry_text":get_entry_text,
setters = {"entry"}
+
class App:
def __init__(self):
@@ -99,7 +108,10 @@ class App:
self.positionLabel = self.builder.get_object("label3")
self.grid = self.builder.get_object("grid1")
self.notebook = self.builder.get_object("notebook1")
-
+ self.info_label = self.builder.get_object("label_status")
+ self.progress_bar = self.builder.get_object("progressbar")
+ self.progress_bar.set_show_text(True)
+
## Event handling ##
self.builder.connect_signals(self)
@@ -115,7 +127,7 @@ class App:
########################################
## DATA ##
########################################
- self.stuff = {} # CirkuixObj's by name
+ self.stuff = {} # CirkuixObj's by name
self.mouse = None
@@ -131,7 +143,7 @@ class App:
def plot_setup(self):
self.figure = Figure(dpi=50)
- self.axes = self.figure.add_axes([0.05,0.05,0.9,0.9])
+ self.axes = self.figure.add_axes([0.05, 0.05, 0.9, 0.9])
self.axes.set_aspect(1)
#t = arange(0.0,5.0,0.01)
#s = sin(2*pi*t)
@@ -148,23 +160,29 @@ class App:
########################################
self.canvas.mpl_connect('button_press_event', self.on_click_over_plot)
self.canvas.mpl_connect('motion_notify_event', self.on_mouse_move_over_plot)
- self.canvas.set_can_focus(True) # For key press
+ self.canvas.set_can_focus(True) # For key press
self.canvas.mpl_connect('key_press_event', self.on_key_over_plot)
self.canvas.mpl_connect('scroll_event', self.on_scroll_over_plot)
- self.grid.attach(self.canvas,0,0,600,400)
-
+ self.grid.attach(self.canvas, 0, 0, 600, 400)
+
+ def info(self, text):
+ """
+ Show text on the status bar.
+ """
+ self.info_label.set_text(text)
+
def zoom(self, factor, center=None):
- '''
+ """
Zooms the plot by factor around a given
center point. Takes care of re-drawing.
- '''
+ """
xmin, xmax = self.axes.get_xlim()
ymin, ymax = self.axes.get_ylim()
width = xmax-xmin
height = ymax-ymin
- if center == None:
+ if center is None:
center = [(xmin+xmax)/2.0, (ymin+ymax)/2.0]
# For keeping the point at the pointer location
@@ -225,10 +243,7 @@ class App:
def plot_cncjob(self, job):
#job.gcode_parse()
- tooldia_text = self.builder.get_object("entry_tooldia").get_text()
- tooldia_val = eval(tooldia_text)
- job.plot2(self.axes, tooldia=tooldia_val)
-
+ job.plot2(self.axes)
self.canvas.queue_draw()
def plot_geometry(self, geometry):
@@ -331,6 +346,10 @@ class App:
self.axes.cla()
self.canvas.queue_draw()
+ def get_eval(self, widget_name):
+ value = self.builder.get_object(widget_name).get_text()
+ return eval(value)
+
########################################
## EVENT HANDLERS ##
########################################
@@ -356,7 +375,38 @@ class App:
self.stuff[iso_name] = geo
self.build_list()
+ def on_generate_cncjob(self, widget):
+ print "Generating CNC job"
+ # Get required info
+ cutz = self.get_eval("entry_geometry_cutz")
+ travelz = self.get_eval("entry_geometry_travelz")
+ feedrate = self.get_eval("entry_geometry_feedrate")
+ geometry = self.stuff[self.selected_item_name]
+ job_name = self.selected_item_name + "_cnc"
+ job = CirkuixCNCjob(job_name, z_move = travelz, z_cut = cutz, feedrate = feedrate)
+ job.generate_from_geometry(geometry.solid_geometry)
+ job.gcode_parse()
+ job.create_geometry()
+
+ # Add to App and update.
+ self.stuff[job_name] = job
+ self.build_list()
+
+ def on_cncjob_tooldia_activate(self, widget):
+ job = self.stuff[self.selected_item_name]
+ tooldia = self.get_eval("entry_cncjob_tooldia")
+ job.tooldia = tooldia
+ print "Changing tool diameter to:", tooldia
+
+ def on_cncjob_exportgcode(self, widget):
+ def on_success(self, filename):
+ cncjob = self.stuff[self.selected_item_name]
+ f = open(filename, 'w')
+ f.write(cncjob.gcode)
+ f.close()
+ print "Saved to:", filename
+ self.file_chooser_save_action(on_success)
def on_delete(self, widget):
self.stuff.pop(self.selected_item_name)
@@ -426,7 +476,7 @@ class App:
def file_chooser_action(self, on_success):
'''
- Opens the file chooser and runs on_success
+ Opens the file chooser and runs on_success on a separate thread
upon completion of valid file choice.
'''
dialog = Gtk.FileChooserDialog("Please choose a file", self.window,
@@ -435,50 +485,130 @@ class App:
Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
response = dialog.run()
if response == Gtk.ResponseType.OK:
- on_success(self, dialog.get_filename())
+ filename = dialog.get_filename()
+ dialog.destroy()
+ t = threading.Thread(target=on_success, args=(self, filename))
+ t.daemon = True
+ t.start()
+ #on_success(self, filename)
elif response == Gtk.ResponseType.CANCEL:
print("Cancel clicked")
- dialog.destroy()
+ dialog.destroy()
+
+ def file_chooser_save_action(self, on_success):
+ '''
+ Opens the file chooser and runs on_success
+ upon completion of valid file choice.
+ '''
+ dialog = Gtk.FileChooserDialog("Save file", self.window,
+ Gtk.FileChooserAction.SAVE,
+ (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
+ Gtk.STOCK_SAVE, Gtk.ResponseType.OK))
+ dialog.set_current_name("Untitled")
+ response = dialog.run()
+ if response == Gtk.ResponseType.OK:
+ filename = dialog.get_filename()
+ dialog.destroy()
+ on_success(self, filename)
+ elif response == Gtk.ResponseType.CANCEL:
+ print("Cancel clicked")
+ dialog.destroy()
def on_fileopengerber(self, param):
def on_success(self, filename):
+ self.progress_bar.set_text("Opening Gerber ...")
+ self.progress_bar.set_fraction(0.1)
+
name = filename.split('/')[-1].split('\\')[-1]
gerber = CirkuixGerber(name)
+
+ self.progress_bar.set_text("Parsing ...")
+ self.progress_bar.set_fraction(0.2)
+
gerber.parse_file(filename)
self.store.append([name])
- #self.gerbers.append(gerber)
self.stuff[name] = gerber
+
+ self.progress_bar.set_text("Plotting ...")
+ self.progress_bar.set_fraction(0.6)
+
self.plot_gerber(gerber)
self.on_zoom_fit(None)
+
+ self.progress_bar.set_text("Done!")
+ self.progress_bar.set_fraction(1.0)
+
+ def clear_bar(bar):
+ bar.set_text("")
+ bar.set_fraction(0.0)
+ threading.Timer(1, clear_bar, args=(self.progress_bar,)).start()
self.file_chooser_action(on_success)
def on_fileopenexcellon(self, param):
def on_success(self, filename):
+ self.progress_bar.set_text("Opening Excellon ...")
+ self.progress_bar.set_fraction(0.1)
+
name = filename.split('/')[-1].split('\\')[-1]
excellon = CirkuixExcellon(name)
+
+ self.progress_bar.set_text("Parsing ...")
+ self.progress_bar.set_fraction(0.2)
+
excellon.parse_file(filename)
self.store.append([name])
- #self.excellons.append(excellon)
self.stuff[name] = excellon
+
+ self.progress_bar.set_text("Plotting ...")
+ self.progress_bar.set_fraction(0.6)
+
self.plot_excellon(excellon)
self.on_zoom_fit(None)
+
+ self.progress_bar.set_text("Done!")
+ self.progress_bar.set_fraction(1.0)
+
+ def clear_bar(bar):
+ bar.set_text("")
+ bar.set_fraction(0.0)
+ threading.Timer(1, clear_bar, args=(self.progress_bar,)).start()
+
self.file_chooser_action(on_success)
def on_fileopengcode(self, param):
def on_success(self, filename):
+ self.progress_bar.set_text("Opening G-Code ...")
+ self.progress_bar.set_fraction(0.1)
+
name = filename.split('/')[-1].split('\\')[-1]
f = open(filename)
gcode = f.read()
f.close()
- job = CirkuixCNCjob(name)
+ tooldia = self.get_eval("entry_tooldia")
+ job = CirkuixCNCjob(name, tooldia=tooldia)
job.gcode = gcode
+
+ self.progress_bar.set_text("Parsing ...")
+ self.progress_bar.set_fraction(0.2)
+
job.gcode_parse()
job.create_geometry()
self.store.append([name])
- #self.cncjobs.append(job)
self.stuff[name] = job
+
+ self.progress_bar.set_text("Plotting ...")
+ self.progress_bar.set_fraction(0.6)
+
self.plot_cncjob(job)
self.on_zoom_fit(None)
+
+ self.progress_bar.set_text("Done!")
+ self.progress_bar.set_fraction(1.0)
+
+ def clear_bar(bar):
+ bar.set_text("")
+ bar.set_fraction(0.0)
+ threading.Timer(1, clear_bar, args=(self.progress_bar,)).start()
self.file_chooser_action(on_success)
def on_mouse_move_over_plot(self, event):
diff --git a/cirkuix.ui b/cirkuix.ui
index dc57f7f1..b7e52544 100644
--- a/cirkuix.ui
+++ b/cirkuix.ui
@@ -144,13 +144,77 @@
-
+
+
+ False
+ True
+ 6
+
-
+
+ True
+ False
+ 3
+ 0
+ 3
+ Export G-Code:
+
+
+
+
+
+ False
+ True
+ 7
+
-
+
+ Export
+ True
+ True
+ True
+
+
+
+
+ False
+ True
+ 8
+
@@ -429,13 +493,131 @@
-
+
+ True
+ False
+ 5
+ 0
+ 3
+ Create CNC Job:
+
+
+
+
+
+ False
+ True
+ 6
+
-
+
+ True
+ False
+ 2
+ 4
+
+
+ True
+ True
+ ●
+
+
+ 1
+ 0
+ 1
+ 1
+
+
+
+
+ True
+ True
+ ●
+
+
+ 1
+ 1
+ 1
+ 1
+
+
+
+
+ True
+ True
+ ●
+
+
+ 1
+ 2
+ 1
+ 1
+
+
+
+
+ True
+ False
+ 1
+ Cut Z:
+
+
+ 0
+ 0
+ 1
+ 1
+
+
+
+
+ True
+ False
+ 1
+ Travel Z:
+
+
+ 0
+ 1
+ 1
+ 1
+
+
+
+
+ True
+ False
+ 1
+ Feed rate:
+
+
+ 0
+ 2
+ 1
+ 1
+
+
+
+
+ False
+ True
+ 7
+
-
+
+ Generate
+ True
+ True
+ True
+
+
+
+
+ False
+ True
+ 8
+
@@ -592,6 +774,7 @@
True
False
+ 5
0
3
Isolation Routing:
@@ -1098,7 +1281,7 @@
True
False
- Options
+ Defaults
False
@@ -1185,10 +1368,30 @@
3
3
-
+
True
False
- Cmd:
+ True
+ 0
+ out
+
+
+ True
+ False
+ 12
+
+
+ True
+ False
+ True
+ 0
+
+
+
+
+
+
+
False
@@ -1197,26 +1400,31 @@
-
+
+ 120
True
- True
- ●
+ False
+ 5
+ 5
+ X: 0.0 Y: 0.0
- True
+ False
True
1
-
- 200
+
+ 50
+ 10
True
False
- 5
- 5
- 0
- X: 0.0 Y: 0.0
+ 2
+ 2
+ 2
+ 2
+ Idle
False
diff --git a/test_excellon_1.py b/test_excellon_1.py
deleted file mode 100644
index 72656b61..00000000
--- a/test_excellon_1.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-Created on Sun Jan 05 13:30:47 2014
-
-@author: jpcaram
-"""
-
-from camlib import *
-#from matplotlib.figure import Figure
-from matplotlib import pyplot
-
-# Gerber. To see if the Excellon is correct
-project_dir = "C:/Users/jpcaram/Dropbox/VNA/KiCad_Squarer/"
-gerber_filename = project_dir + "KiCad_Squarer-F_Cu.gtl"
-g = Gerber()
-g.parse_file(gerber_filename)
-g.create_geometry()
-
-excellon_filename = project_dir + "KiCad_Squarer.drl"
-ex = Excellon()
-ex.parse_file(excellon_filename)
-ex.create_geometry()
-
-#fig = Figure()
-fig = pyplot.figure()
-ax = fig.add_subplot(111)
-ax.set_aspect(1)
-
-# Plot gerber
-for geo in g.solid_geometry:
- x, y = geo.exterior.coords.xy
- plot(x, y, 'k-')
- for ints in geo.interiors:
- x, y = ints.coords.xy
- ax.plot(x, y, 'k-')
-
-# Plot excellon
-for geo in ex.solid_geometry:
- x, y = geo.exterior.coords.xy
- plot(x, y, 'r-')
- for ints in geo.interiors:
- x, y = ints.coords.xy
- ax.plot(x, y, 'g-')
-
-fig.show()
\ No newline at end of file