Initial implementation of console.

This commit is contained in:
Juan Pablo Caram
2014-09-13 17:29:07 -04:00
parent 32076d4020
commit 8cb509d6f3
7 changed files with 526 additions and 14 deletions

View File

@@ -8,6 +8,7 @@ import simplejson as json
import re
import webbrowser
import os
import Tkinter
from PyQt4 import QtCore
@@ -22,6 +23,8 @@ from FlatCAMGUI import *
from FlatCAMCommon import LoudDict
from FlatCAMTool import *
from FlatCAMShell import FCShell
########################################
## App ##
@@ -251,7 +254,7 @@ class App(QtCore.QObject):
#### Check for updates ####
# Separate thread (Not worker)
self.version = 6
self.version = 7
App.log.info("Checking for updates in backgroud (this is version %s)." % str(self.version))
self.worker2 = Worker(self, name="worker2")
@@ -293,6 +296,7 @@ class App(QtCore.QObject):
self.ui.menuviewdisableall.triggered.connect(self.disable_plots)
self.ui.menuviewdisableother.triggered.connect(lambda: self.disable_plots(except_current=True))
self.ui.menuviewenable.triggered.connect(self.enable_all_plots)
self.ui.menutoolshell.triggered.connect(lambda: self.shell.show())
self.ui.menuhelp_about.triggered.connect(self.on_about)
self.ui.menuhelp_manual.triggered.connect(lambda: webbrowser.open(self.app_url))
# Toolbar
@@ -302,6 +306,7 @@ class App(QtCore.QObject):
self.ui.clear_plot_btn.triggered.connect(self.plotcanvas.clear)
self.ui.replot_btn.triggered.connect(self.on_toolbar_replot)
self.ui.delete_btn.triggered.connect(self.on_delete)
self.ui.shell_btn.triggered.connect(lambda: self.shell.show())
# Object list
self.collection.view.activated.connect(self.on_row_activated)
# Options
@@ -324,6 +329,20 @@ class App(QtCore.QObject):
self.measeurement_tool = Measurement(self)
self.measeurement_tool.install()
#############
### Shell ###
#############
# TODO: Move this to its own class
self.shell = FCShell(self)
self.shell.setWindowIcon(self.ui.app_icon)
self.shell.setWindowTitle("FlatCAM Shell")
self.shell.show()
self.shell.resize(550, 300)
self.shell.append_output("FlatCAM Alpha 7\n(c) 2014 Juan Pablo Caram\n\n")
self.shell.append_output("Type help to get started.\n\n")
self.tcl = Tkinter.Tcl()
self.setup_shell()
App.log.debug("END of constructor. Releasing control.")
def defaults_read_form(self):
@@ -368,6 +387,110 @@ class App(QtCore.QObject):
# Send to worker
self.worker_task.emit({'fcn': worker_task, 'params': [self]})
def execCommand(self, text):
"""
Hadles input from the shell.
:param text: Input command
:return: None
"""
text = str(text)
try:
result = self.tcl.eval(str(text))
self.shell.append_output(result + '\n')
except Tkinter.TclError, e:
self.shell.append_error('ERROR: ' + str(e) + '\n')
raise
return
def shhelp(p=None):
if not p:
return "Available commands:\n" + '\n'.join([' ' + cmd for cmd in commands])
if p not in commands:
return "Unknown command: %s" % p
return commands[p]["help"]
commands = {
"open_gerber": {
"fcn": self.open_gerber,
"params": 1,
"converters": [lambda x: x],
"retfcn": lambda x: None,
"help": "Opens a Gerber file.\n> open_gerber <filename>\n filename: Path to file to open."
},
"open_excellon": {
"fcn": self.open_excellon,
"params": 1,
"converters": [lambda x: x],
"retfcn": lambda x: None,
"help": "Opens an Excellon file.\n> open_excellon <filename>\n filename: Path to file to open."
},
"open_gcode": {
"fcn": self.open_gcode,
"params": 1,
"converters": [lambda x: x],
"retfcn": lambda x: None,
"help": "Opens an G-Code file.\n> open_gcode <filename>\n filename: Path to file to open."
},
"open_project": {
"fcn": self.open_project,
"params": 1,
"converters": [lambda x: x],
"retfcn": lambda x: None,
"help": "Opens a FlatCAM project.\n> open_project <filename>\n filename: Path to file to open."
},
"save_project": {
"fcn": self.save_project,
"params": 1,
"converters": [lambda x: x],
"retfcn": lambda x: None,
"help": "Saves the FlatCAM project to file.\n> save_project <filename>\n filename: Path to file to save."
},
"help": {
"fcn": shhelp,
"params": [0, 1],
"converters": [lambda x: x],
"retfcn": lambda x: x,
"help": "Shows list of commands."
}
}
parts = re.findall(r'([\w\\:\.]+|".*?")+', text)
parts = [p.replace('\n', '').replace('"', '') for p in parts]
self.log.debug(parts)
try:
if parts[0] not in commands:
self.shell.append_error("Unknown command\n")
return
#import inspect
#inspect.getargspec(someMethod)
if (type(commands[parts[0]]["params"]) is not list and len(parts)-1 != commands[parts[0]]["params"]) or \
(type(commands[parts[0]]["params"]) is list and len(parts)-1 not in commands[parts[0]]["params"]):
self.shell.append_error(
"Command %s takes %d arguments. %d given.\n" %
(parts[0], commands[parts[0]]["params"], len(parts)-1)
)
return
cmdfcn = commands[parts[0]]["fcn"]
cmdconv = commands[parts[0]]["converters"]
if len(parts)-1 > 0:
retval = cmdfcn(*[cmdconv[i](parts[i+1]) for i in range(len(parts)-1)])
else:
retval = cmdfcn()
retfcn = commands[parts[0]]["retfcn"]
if retval and retfcn(retval):
self.shell.append_output(retfcn(retval) + "\n")
except:
self.shell.append_error(''.join(traceback.format_exc()))
#self.shell.append_error("?\n")
def info(self, text):
self.ui.info_label.setText(QtCore.QString(text))
@@ -540,7 +663,7 @@ class App(QtCore.QObject):
title = QtGui.QLabel(
"<font size=8><B>FlatCAM</B></font><BR>"
"Version Alpha 6 (2014/09)<BR>"
"Version Alpha 7 (2014/10)<BR>"
"<BR>"
"2D Post-processing for Manufacturing specialized in<BR>"
"Printed Circuit Boards<BR>"
@@ -1379,6 +1502,114 @@ class App(QtCore.QObject):
def set_progress_bar(self, percentage, text=""):
self.ui.progress_bar.setValue(int(percentage))
def setup_shell(self):
self.log.debug("setup_shell()")
def shelp(p=None):
if not p:
return "Available commands:\n" + '\n'.join([' ' + cmd for cmd in commands]) + \
"\n\nType help <command_name> for usage.\n Example: help open_gerber"
if p not in commands:
return "Unknown command: %s" % p
return commands[p]["help"]
def options(name):
ops = self.collection.get_by_name(str(name)).options
return '\n'.join(["%s: %s" % (o, ops[o]) for o in ops])
def isolate(name, dia=None, passes=None, overlap=None):
dia = float(dia) if dia is not None else None
passes = int(passes) if passes is not None else None
overlap = float(overlap) if overlap is not None else None
self.collection.get_by_name(str(name)).isolate(dia, passes, overlap)
commands = {
'help': {
'fcn': shelp,
'help': "Shows list of commands."
},
'open_gerber': {
'fcn': self.open_gerber,
'help': "Opens a Gerber file.\n> open_gerber <filename>\n filename: Path to file to open."
},
'open_excellon': {
'fcn': self.open_excellon,
'help': "Opens an Excellon file.\n> open_excellon <filename>\n filename: Path to file to open."
},
'open_gcode': {
'fcn': self.open_gcode,
'help': "Opens an G-Code file.\n> open_gcode <filename>\n filename: Path to file to open."
},
'open_project': {
'fcn': self.open_project,
"help": "Opens a FlatCAM project.\n> open_project <filename>\n filename: Path to file to open."
},
'save_project': {
'fcn': self.save_project,
'help': "Saves the FlatCAM project to file.\n> save_project <filename>\n filename: Path to file to save."
},
'set_active': {
'fcn': self.collection.set_active,
'help': "Sets a FlatCAM object as active.\n > set_active <name>\n name: Name of the object."
},
'get_names': {
'fcn': lambda: '\n'.join(self.collection.get_names()),
'help': "Lists the names of objects in the project.\n > get_names"
},
'new': {
'fcn': self.on_file_new,
'help': "Starts a new project. Clears objects from memory.\n > new"
},
'options': {
'fcn': options,
'help': "Shows the settings for an object.\n > options <name>\n name: Object name."
},
'isolate': {
'fcn': isolate,
'help': "Creates isolation routing geometry for the given Gerber.\n" +
"> isolate <name> [dia [passes [overlap]]]\n" +
" name: Name if 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 <name> <factor>\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 <name> <x> <y>\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'
}
}
for cmd in commands:
self.tcl.createcommand(cmd, commands[cmd]['fcn'])
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()")
@@ -1473,8 +1704,8 @@ class App(QtCore.QObject):
try:
data = json.load(f)
except:
App.log.error("Could nor parse information about latest version.")
self.inform.emit("Could nor parse information about latest version.")
App.log.error("Could not parse information about latest version.")
self.inform.emit("Could not parse information about latest version.")
f.close()
return