] [-passes ] [-overlap ]\n" +
" name: Name of the object\n"
" dia: Tool diameter\n passes: # of tool width\n" +
" overlap: Fraction of tool diameter to overlap passes"
},
'scale': {
'fcn': lambda name, factor: self.collection.get_by_name(str(name)).scale(float(factor)),
'help': "Resizes the object by a factor.\n" +
"> scale \n" +
" name: Name of the object\n factor: Fraction by which to scale"
},
'offset': {
'fcn': lambda name, x, y: self.collection.get_by_name(str(name)).offset([float(x), float(y)]),
'help': "Changes the position of the object.\n" +
"> offset \n" +
" name: Name of the object\n" +
" x: X-axis distance\n" +
" y: Y-axis distance"
},
'plot': {
'fcn': self.plot_all,
'help': 'Updates the plot on the user interface'
},
'cncjob': {
'fcn': cncjob,
'help': 'Generates a CNC Job from a Geometry Object.\n' +
'> cncjob [-z_cut ] [-z_move ] [-feedrate ] [-tooldia ] [-outname ]\n' +
' name: Name of the source object\n' +
' z_cut: Z-axis cutting position\n' +
' z_move: Z-axis moving position\n' +
' feedrate: Moving speed when cutting\n' +
' tooldia: Tool diameter to show on screen\n' +
' outname: Name of the output object'
},
'write_gcode': {
'fcn': write_gcode,
'help': 'Saves G-code of a CNC Job object to file.\n' +
'> write_gcode \n' +
' name: Source CNC Job object\n' +
' filename: Output filename'
},
'paint_poly': {
'fcn': paint_poly,
'help': 'Creates a geometry object with toolpath to cover the inside of a polygon.\n' +
'> paint_poly \n' +
' name: Name of the sourge geometry object.\n' +
' inside_pt_x, inside_pt_y: Coordinates of a point inside the polygon.\n' +
' tooldia: Diameter of the tool to be used.\n' +
' overlap: Fraction of the tool diameter to overlap cuts.'
},
'new_geometry': {
'fcn': lambda name: self.new_object('geometry', str(name), lambda x, y: None),
'help': 'Creates a new empty geometry object.\n' +
'> new_geometry \n' +
' name: New object name'
},
'add_poly': {
'fcn': add_poly,
'help': 'Creates a polygon in the given Geometry object.\n' +
'> create_poly [x3 y3 [...]]\n' +
' name: Name of the geometry object to which to append the polygon.\n' +
' xi, yi: Coordinates of points in the polygon.'
},
'delete': {
'fcn': delete,
'help': 'Deletes the give object.\n' +
'> delete \n' +
' name: Name of the object to delete.'
},
'geo_union': {
'fcn': geo_union,
'help': 'Runs a union operation (addition) on the components ' +
'of the geometry object. For example, if it contains ' +
'2 intersecting polygons, this opperation adds them into' +
'a single larger polygon.\n' +
'> geo_union \n' +
' name: Name of the geometry object.'
},
'add_rect': {
'fcn': add_rectangle,
'help': 'Creates a rectange in the given Geometry object.\n' +
'> add_rect \n' +
' name: Name of the geometry object to which to append the rectangle.\n' +
' botleft_x, botleft_y: Coordinates of the bottom left corner.\n' +
' topright_x, topright_y Coordinates of the top right corner.'
},
'add_circle': {
'fcn': add_circle,
'help': 'Creates a circle in the given Geometry object.\n' +
'> add_circle \n' +
' name: Name of the geometry object to which to append the circle.\n' +
' center_x, center_y: Coordinates of the center of the circle.\n' +
' radius: Radius of the circle.'
},
'make_docs': {
'fcn': make_docs,
'help': 'Prints command rererence in reStructuredText format.'
},
'follow': {
'fcn': follow,
'help': 'Creates a geometry object following gerber paths.\n' +
'> follow [-outname ]\n' +
' name: Name of the gerber object.\n' +
' outname: Name of the output geometry object.'
},
'get_sys': {
'fcn': get_sys,
'help': 'Get the value of a system parameter (FlatCAM constant)\n' +
'> get_sys \n' +
' sysparam: Name of the parameter.'
},
'set_sys': {
'fcn': set_sys,
'help': 'Set the value of a system parameter (FlatCAM constant)\n' +
'> set_sys \n' +
' sysparam: Name of the parameter.\n' +
' paramvalue: Value to set.'
}
}
# Add commands to the tcl interpreter
for cmd in commands:
self.tcl.createcommand(cmd, commands[cmd]['fcn'])
# Make the tcl puts function return instead of print to stdout
self.tcl.eval('''
rename puts original_puts
proc puts {args} {
if {[llength $args] == 1} {
return "[lindex $args 0]"
} else {
eval original_puts $args
}
}
''')
def setup_recent_items(self):
self.log.debug("setup_recent_items()")
# TODO: Move this to constructor
icons = {
"gerber": "share/flatcam_icon16.png",
"excellon": "share/drill16.png",
"cncjob": "share/cnc16.png",
"project": "share/project16.png"
}
openers = {
'gerber': lambda fname: self.worker_task.emit({'fcn': self.open_gerber, 'params': [fname]}),
'excellon': lambda fname: self.worker_task.emit({'fcn': self.open_excellon, 'params': [fname]}),
'cncjob': lambda fname: self.worker_task.emit({'fcn': self.open_gcode, 'params': [fname]}),
'project': self.open_project
}
# Open file
try:
f = open(self.data_path + '/recent.json')
except IOError:
App.log.error("Failed to load recent item list.")
self.inform.emit("[error] Failed to load recent item list.")
return
try:
self.recent = json.load(f)
except json.scanner.JSONDecodeError:
App.log.error("Failed to parse recent item list.")
self.inform.emit("[error] Failed to parse recent item list.")
f.close()
return
f.close()
# Closure needed to create callbacks in a loop.
# Otherwise late binding occurs.
def make_callback(func, fname):
def opener():
func(fname)
return opener
# Reset menu
self.ui.recent.clear()
# Create menu items
for recent in self.recent:
filename = recent['filename'].split('/')[-1].split('\\')[-1]
action = QtGui.QAction(QtGui.QIcon(icons[recent["kind"]]), filename, self)
# Attach callback
o = make_callback(openers[recent["kind"]], recent['filename'])
action.triggered.connect(o)
self.ui.recent.addAction(action)
# self.builder.get_object('open_recent').set_submenu(recent_menu)
# self.ui.menufilerecent.set_submenu(recent_menu)
# recent_menu.show_all()
# self.ui.recent.show()
def setup_component_editor(self):
label = QtGui.QLabel("Choose an item from Project")
label.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
self.ui.selected_scroll_area.setWidget(label)
def setup_obj_classes(self):
"""
Sets up application specifics on the FlatCAMObj class.
:return: None
"""
FlatCAMObj.app = self
def version_check(self):
"""
Checks for the latest version of the program. Alerts the
user if theirs is outdated. This method is meant to be run
in a separate thread.
:return: None
"""
self.log.debug("version_check()")
full_url = App.version_url + \
"?s=" + str(self.defaults['serial']) + \
"&v=" + str(self.version) + \
"&" + urllib.urlencode(self.defaults["stats"])
App.log.debug("Checking for updates @ %s" % full_url)
try:
f = urllib.urlopen(full_url)
except:
App.log.warning("Failed checking for latest version. Could not connect.")
self.inform.emit("[warning] Failed checking for latest version. Could not connect.")
return
try:
data = json.load(f)
except Exception, e:
App.log.error("Could not parse information about latest version.")
self.inform.emit("[error] Could not parse information about latest version.")
App.log.debug("json.load(): %s" % str(e))
f.close()
return
f.close()
if self.version >= data["version"]:
App.log.debug("FlatCAM is up to date!")
self.inform.emit("[success] FlatCAM is up to date!")
return
App.log.debug("Newer version available.")
self.message.emit(
"Newer Version Available",
"There is a newer version of FlatCAM\n" +
"available for download:\n\n" +
data["name"] + "\n\n" + data["message"],
"info"
)
def enable_all_plots(self, *args):
self.plotcanvas.clear()
def worker_task(app_obj):
percentage = 0.1
try:
delta = 0.9 / len(self.collection.get_list())
except ZeroDivisionError:
self.progress.emit(0)
return
for obj in self.collection.get_list():
obj.options['plot'] = True
obj.plot()
percentage += delta
self.progress.emit(int(percentage*100))
self.progress.emit(0)
self.plots_updated.emit()
# Send to worker
# self.worker.add_task(worker_task, [self])
self.worker_task.emit({'fcn': worker_task, 'params': [self]})
def save_project(self, filename):
"""
Saves the current project to the specified file.
:param filename: Name of the file in which to save.
:type filename: str
:return: None
"""
self.log.debug("save_project()")
## Capture the latest changes
# Current object
try:
self.collection.get_active().read_form()
except:
self.log.debug("[warning] There was no active object")
pass
# Project options
self.options_read_form()
# Serialize the whole project
d = {"objs": [obj.to_dict() for obj in self.collection.get_list()],
"options": self.options,
"version": self.version}
# Open file
try:
f = open(filename, 'w')
except IOError:
App.log.error("[error] Failed to open file for saving:", filename)
return
# Write
try:
json.dump(d, f, default=to_dict)
except:
App.log.error("[error] File open but failed to write:", filename)
f.close()
return
f.close()
self.inform.emit("Project saved to: %s" % filename)
# def main():
#
# app = QtGui.QApplication(sys.argv)
# fc = App()
# sys.exit(app.exec_())
#
#
# if __name__ == '__main__':
# main()