- changed the name of the new object FlatCAMNotes to a more general one FlatCAMDocument

- changed the way a new FlatCAMScript object is made, the method that is processing the Tcl commands when the Run button is clicked is moved to the FlatCAMObj.FlatCAMScript() class
- reused the Multiprocessing Pool declared in the App for the ToolRulesCheck() class
- adapted the Project context menu for the new types of FLatCAM objects
- modified the setup_recent_files to accommodate the new FlatCAM objects
- made sure that when an FlatCAM script object is deleted, it's associated Tab is closed
This commit is contained in:
Marius Stanciu 2019-10-02 17:58:48 +03:00
parent 83f229ed9e
commit a75bdfb29d
8 changed files with 541 additions and 816 deletions

View File

@ -29,7 +29,7 @@ import gc
from xml.dom.minidom import parseString as parse_xml_string
from multiprocessing.connection import Listener, Client
from multiprocessing import Pool
from multiprocessing import Pool, cpu_count
import socket
from array import array
@ -376,7 +376,7 @@ class App(QtCore.QObject):
# #############################################################################
# ##################### CREATE MULTIPROCESSING POOL ###########################
# #############################################################################
self.pool = Pool()
self.pool = Pool(processes=cpu_count())
# ##########################################################################
# ################## Setting the Splash Screen #############################
@ -1227,9 +1227,14 @@ class App(QtCore.QObject):
'minoffset, multidepth, name, offset, opt_type, order, outname, overlap, '
'passes, postamble, pp, ppname_e, ppname_g, preamble, radius, ref, rest, '
'rows, shellvar_, scale_factor, spacing_columns, spacing_rows, spindlespeed, '
'toolchange_xy, use_threads, value, x, x0, x1, y, y0, y1, z_cut, z_move'
,
'toolchange_xy, use_threads, value, x, x0, x1, y, y0, y1, z_cut, z_move',
"script_autocompleter": True,
"script_text": "",
"script_plot": True,
"script_source_file": "",
"document_text": "",
"document_plot": True,
"document_source_file": "",
})
# ############################################################
@ -1514,9 +1519,11 @@ class App(QtCore.QObject):
"tools_panelize_constrainy": 0.0,
"script_text": "",
"script_plot": True,
"notes_text": "",
"notes_plot": True,
"script_plot": False,
"script_source_file": "",
"document_text": "",
"document_plot": False,
"document_source_file": "",
})
@ -3955,7 +3962,7 @@ class App(QtCore.QObject):
"cncjob": FlatCAMCNCjob,
"geometry": FlatCAMGeometry,
"script": FlatCAMScript,
"notes": FlatCAMNotes
"document": FlatCAMDocument
}
App.log.debug("Calling object constructor...")
@ -4019,8 +4026,8 @@ class App(QtCore.QObject):
self.log.debug("%f seconds converting units." % (t3 - t2))
# Create the bounding box for the object and then add the results to the obj.options
# But not for Scripts or for Notes
if kind != 'notes' and kind != 'script':
# But not for Scripts or for Documents
if kind != 'document' and kind != 'script':
try:
xmin, ymin, xmax, ymax = obj.bounds()
obj.options['xmin'] = xmin
@ -4031,6 +4038,9 @@ class App(QtCore.QObject):
log.warning("The object has no bounds properties. %s" % str(e))
return "fail"
# update the KeyWords list with the name of the file
self.myKeywords.append(obj.options['name'])
FlatCAMApp.App.log.debug("Moving new object back to main thread.")
# Move the object to the main thread and let the app know that it is available.
@ -4089,7 +4099,7 @@ class App(QtCore.QObject):
self.new_object('gerber', 'new_grb', initialize, plot=False)
def new_script_object(self, name=None):
def new_script_object(self, name=None, text=None):
"""
Creates a new, blank TCL Script object.
:param name: a name for the new object
@ -4097,21 +4107,30 @@ class App(QtCore.QObject):
"""
self.report_usage("new_script_object()")
def initialize(obj, self):
obj.source_file = _(
"#\n"
"# CREATE A NEW FLATCAM TCL SCRIPT\n"
"# TCL Tutorial here: https://www.tcl.tk/man/tcl8.5/tutorial/tcltutorial.html\n"
"#\n\n"
"# FlatCAM commands list:\n"
"# AddCircle, AddPolygon, AddPolyline, AddRectangle, AlignDrill, AlignDrillGrid, ClearShell, ClearCopper,\n"
"# Cncjob, Cutout, Delete, Drillcncjob, ExportGcode, ExportSVG, Exteriors, GeoCutout, GeoUnion, GetNames,\n"
"# GetSys, ImportSvg, Interiors, Isolate, Follow, JoinExcellon, JoinGeometry, ListSys, MillDrills,\n"
"# MillSlots, Mirror, New, NewGeometry, Offset, OpenExcellon, OpenGCode, OpenGerber, OpenProject,\n"
"# Options, Paint, Panelize, Plot, SaveProject, SaveSys, Scale, SetActive, SetSys, Skew, SubtractPoly,\n"
"# SubtractRectangle, Version, WriteGCode\n"
"#\n\n"
if text is not None:
new_source_file = text
else:
new_source_file = _(
"#\n"
"# CREATE A NEW FLATCAM TCL SCRIPT\n"
"# TCL Tutorial here: https://www.tcl.tk/man/tcl8.5/tutorial/tcltutorial.html\n"
"#\n\n"
"# FlatCAM commands list:\n"
"# AddCircle, AddPolygon, AddPolyline, AddRectangle, AlignDrill, AlignDrillGrid, ClearShell, "
"ClearCopper,\n"
"# Cncjob, Cutout, Delete, Drillcncjob, ExportGcode, ExportSVG, Exteriors, GeoCutout, GeoUnion, "
"GetNames,\n"
"# GetSys, ImportSvg, Interiors, Isolate, Follow, JoinExcellon, JoinGeometry, ListSys, MillDrills,\n"
"# MillSlots, Mirror, New, NewGeometry, Offset, OpenExcellon, OpenGCode, OpenGerber, OpenProject,\n"
"# Options, Paint, Panelize, Plot, SaveProject, SaveSys, Scale, SetActive, SetSys, Skew, "
"SubtractPoly,\n"
"# SubtractRectangle, Version, WriteGCode\n"
"#\n\n"
)
def initialize(obj, self):
obj.source_file = deepcopy(new_source_file)
if name is None:
outname = 'new_script'
else:
@ -4119,19 +4138,18 @@ class App(QtCore.QObject):
self.new_object('script', outname, initialize, plot=False)
def new_notes_object(self):
def new_document_object(self):
"""
Creates a new, blank Notes object.
Creates a new, blank Document object.
:return: None
"""
self.report_usage("new_notes_object()")
self.report_usage("new_document_object()")
def initialize(obj, self):
obj.source_file = ""
self.new_object('notes', 'new_notes', initialize, plot=False)
self.new_object('document', 'new_document', initialize, plot=False)
def on_object_created(self, obj, plot, autoselect):
"""
@ -4169,12 +4187,11 @@ class App(QtCore.QObject):
elif obj.kind == 'script':
self.inform.emit(_('[selected] {kind} created/selected: <span style="color:{color};">{name}</span>').format(
kind=obj.kind.capitalize(), color='orange', name=str(obj.options['name'])))
elif obj.kind == 'notes':
elif obj.kind == 'document':
self.inform.emit(_('[selected] {kind} created/selected: <span style="color:{color};">{name}</span>').format(
kind=obj.kind.capitalize(), color='violet', name=str(obj.options['name'])))
# update the SHELL auto-completer model with the name of the new object
self.myKeywords.append(obj.options['name'])
self.shell._edit.set_model_data(self.myKeywords)
if autoselect:
@ -6971,6 +6988,12 @@ class App(QtCore.QObject):
obj_init.slots = deepcopy(obj.slots)
obj_init.create_geometry()
def initialize_script(obj_init, app_obj):
obj_init.source_file = deepcopy(obj.source_file)
def initialize_document(obj_init, app_obj):
obj_init.source_file = deepcopy(obj.source_file)
for obj in self.collection.get_selected():
obj_name = obj.options["name"]
@ -6981,6 +7004,10 @@ class App(QtCore.QObject):
self.new_object("gerber", str(obj_name) + "_copy", initialize)
elif isinstance(obj, FlatCAMGeometry):
self.new_object("geometry", str(obj_name) + "_copy", initialize)
elif isinstance(obj, FlatCAMScript):
self.new_object("script", str(obj_name) + "_copy", initialize_script)
elif isinstance(obj, FlatCAMDocument):
self.new_object("document", str(obj_name) + "_copy", initialize_document)
except Exception as e:
return "Operation failed: %s" % str(e)
@ -8376,6 +8403,10 @@ class App(QtCore.QObject):
obj.on_exportgcode_button_click()
elif type(obj) == FlatCAMGerber:
self.on_file_savegerber()
elif type(obj) == FlatCAMScript:
self.on_file_savescript()
elif type(obj) == FlatCAMDocument:
self.on_file_savedocument()
def obj_move(self):
self.report_usage("obj_move()")
@ -8694,6 +8725,94 @@ class App(QtCore.QObject):
self.file_opened.emit("Gerber", filename)
self.file_saved.emit("Gerber", filename)
def on_file_savescript(self):
"""
Callback for menu item in Project context menu.
:return: None
"""
self.report_usage("on_file_savescript")
App.log.debug("on_file_savescript()")
obj = self.collection.get_active()
if obj is None:
self.inform.emit('[WARNING_NOTCL] %s' %
_("No object selected. Please select an Script object to export."))
return
# Check for more compatible types and add as required
if not isinstance(obj, FlatCAMScript):
self.inform.emit('[ERROR_NOTCL] %s' %
_("Failed. Only Script objects can be saved as TCL Script files..."))
return
name = self.collection.get_active().options["name"]
_filter = "FlatCAM Scripts (*.FlatScript);;All Files (*.*)"
try:
filename, _f = QtWidgets.QFileDialog.getSaveFileName(
caption="Save Script source file",
directory=self.get_last_save_folder() + '/' + name,
filter=_filter)
except TypeError:
filename, _f = QtWidgets.QFileDialog.getSaveFileName(caption=_("Save Script source file"), filter=_filter)
filename = str(filename)
if filename == "":
self.inform.emit('[WARNING_NOTCL] %s' %
_("Save Script source file cancelled."))
return
else:
self.save_source_file(name, filename)
if self.defaults["global_open_style"] is False:
self.file_opened.emit("Script", filename)
self.file_saved.emit("Script", filename)
def on_file_savedocument(self):
"""
Callback for menu item in Project context menu.
:return: None
"""
self.report_usage("on_file_savedocument")
App.log.debug("on_file_savedocument()")
obj = self.collection.get_active()
if obj is None:
self.inform.emit('[WARNING_NOTCL] %s' %
_("No object selected. Please select an Document object to export."))
return
# Check for more compatible types and add as required
if not isinstance(obj, FlatCAMScript):
self.inform.emit('[ERROR_NOTCL] %s' %
_("Failed. Only Document objects can be saved as Document files..."))
return
name = self.collection.get_active().options["name"]
_filter = "FlatCAM Documents (*.FlatDoc);;All Files (*.*)"
try:
filename, _f = QtWidgets.QFileDialog.getSaveFileName(
caption="Save Document source file",
directory=self.get_last_save_folder() + '/' + name,
filter=_filter)
except TypeError:
filename, _f = QtWidgets.QFileDialog.getSaveFileName(caption=_("Save Document source file"), filter=_filter)
filename = str(filename)
if filename == "":
self.inform.emit('[WARNING_NOTCL] %s' %
_("Save Document source file cancelled."))
return
else:
self.save_source_file(name, filename)
if self.defaults["global_open_style"] is False:
self.file_opened.emit("Document", filename)
self.file_saved.emit("Document", filename)
def on_file_saveexcellon(self):
"""
Callback for menu item in project context menu.
@ -9090,65 +9209,22 @@ class App(QtCore.QObject):
self.inform.emit('[success] %s' %
_("New TCL script file created in Code Editor."))
flt = "FlatCAM Scripts (*.FlatScript);;All Files (*.*)"
self.proc_container.view.set_busy(_("Loading..."))
self.script_editor_tab = TextEditor(app=self)
# add the tab if it was closed
self.ui.plot_tab_area.addTab(self.script_editor_tab, '%s' % _("Script Editor"))
self.script_editor_tab.setObjectName('script_editor_tab')
# delete the absolute and relative position and messages in the infobar
self.ui.position_label.setText("")
self.ui.rel_position_label.setText("")
# first clear previous text in text editor (if any)
self.script_editor_tab.code_editor.clear()
self.script_editor_tab.code_editor.setReadOnly(False)
self.script_editor_tab.code_editor.completer_enable = True
self.script_editor_tab.buttonRun.show()
# Switch plot_area to CNCJob tab
self.ui.plot_tab_area.setCurrentWidget(self.script_editor_tab)
self.script_editor_tab.buttonOpen.clicked.disconnect()
self.script_editor_tab.buttonOpen.clicked.connect(lambda: self.script_editor_tab.handleOpen(filt=flt))
self.script_editor_tab.buttonSave.clicked.disconnect()
self.script_editor_tab.buttonSave.clicked.connect(lambda: self.script_editor_tab.handleSaveGCode(filt=flt))
try:
self.script_editor_tab.buttonRun.clicked.disconnect()
except TypeError:
pass
self.script_editor_tab.buttonRun.clicked.connect(self.script_editor_tab.handleRunCode)
self.script_editor_tab.handleTextChanged()
if name is not None:
self.new_script_object(name=name)
script_obj = self.collection.get_by_name(name)
self.new_script_object(name=name, text=text)
else:
self.new_script_object()
script_obj = self.collection.get_by_name('new_script')
self.new_script_object(text=text)
script_text = script_obj.source_file
self.script_editor_tab.t_frame.hide()
if text is not None:
try:
for line in text:
self.script_editor_tab.code_editor.append(line)
except TypeError:
self.script_editor_tab.code_editor.append(text)
else:
self.script_editor_tab.code_editor.append(script_text)
self.script_editor_tab.t_frame.show()
self.proc_container.view.set_idle()
# script_text = script_obj.source_file
#
# self.proc_container.view.set_busy(_("Loading..."))
# script_obj.script_editor_tab.t_frame.hide()
#
# script_obj.script_editor_tab.t_frame.show()
# self.proc_container.view.set_idle()
def on_fileopenscript(self, name=None, silent=False):
"""
@ -9158,53 +9234,29 @@ class App(QtCore.QObject):
:param name: name of a Tcl script file to open
:return:
"""
script_content = []
self.report_usage("on_fileopenscript")
App.log.debug("on_fileopenscript()")
_filter_ = "TCL script (*.FlatScript);;TCL script (*.TCL);;TCL script (*.TXT);;All Files (*.*)"
if name:
filename = name
filenames = [name]
else:
_filter_ = "TCL script (*.FlatScript);;TCL script (*.TCL);;TCL script (*.TXT);;All Files (*.*)"
try:
filename, _f = QtWidgets.QFileDialog.getOpenFileName(caption=_("Open TCL script"),
filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open TCL script"),
directory=self.get_last_folder(), filter=_filter_)
except TypeError:
filename, _f = QtWidgets.QFileDialog.getOpenFileName(caption=_("Open TCL script"), filter=_filter_)
filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open TCL script"), filter=_filter_)
# The Qt methods above will return a QString which can cause problems later.
# So far json.dump() will fail to serialize it.
# TODO: Improve the serialization methods and remove this fix.
filename = str(filename)
if filename == "":
if len(filenames) == 0:
if silent is False:
self.inform.emit('[WARNING_NOTCL] %s' % _("Open TCL script cancelled."))
else:
self.proc_container.view.set_busy(_("Loading..."))
try:
with open(filename, "r") as opened_script:
try:
for line in opened_script:
QtWidgets.QApplication.processEvents()
proc_line = str(line).strip('\n')
script_content.append(proc_line)
except Exception as e:
log.debug('App.on_fileopenscript() -->%s' % str(e))
if silent is False:
self.inform.emit('[ERROR] %s %s' %
('App.on_fileopenscript() -->', str(e)))
return
if silent is False:
self.inform.emit('[success] %s' % _("TCL script file opened in Code Editor."))
except Exception as e:
log.debug("App.on_fileopenscript() -> %s" % str(e))
self.proc_container.view.set_idle()
script_name = filename.split('/')[-1].split('\\')[-1]
self.on_filenewscript(name=script_name, text=script_content)
for filename in filenames:
if filename != '':
self.worker_task.emit({'fcn': self.open_script, 'params': [filename]})
def on_filerunscript(self, name=None, silent=False):
"""
@ -10353,8 +10405,6 @@ class App(QtCore.QObject):
assert isinstance(app_obj_, App), \
"Initializer expected App, got %s" % type(app_obj_)
self.progress.emit(10)
try:
f = open(filename)
gcode = f.read()
@ -10362,20 +10412,16 @@ class App(QtCore.QObject):
except IOError:
app_obj_.inform.emit('[ERROR_NOTCL] %s: %s' %
(_("Failed to open"), filename))
self.progress.emit(0)
return "fail"
job_obj.gcode = gcode
self.progress.emit(20)
ret = job_obj.gcode_parse()
if ret == "fail":
self.inform.emit('[ERROR_NOTCL] %s' %
_("This is not GCODE"))
return "fail"
self.progress.emit(60)
job_obj.create_geometry()
with self.proc_container.new(_("Opening G-Code.")):
@ -10398,7 +10444,44 @@ class App(QtCore.QObject):
# GUI feedback
self.inform.emit('[success] %s: %s' %
(_("Opened"), filename))
self.progress.emit(100)
def open_script(self, filename, outname=None, silent=False):
"""
Opens a Script file, parses it and creates a new object for
it in the program. Thread-safe.
:param outname: Name of the resulting object. None causes the name to be that of the file.
:param filename: Script file filename
:type filename: str
:return: None
"""
App.log.debug("open_script()")
with self.proc_container.new(_("Opening TCL Script...")):
try:
with open(filename, "r") as opened_script:
script_content = opened_script.readlines()
script_content = ''.join(script_content)
if silent is False:
self.inform.emit('[success] %s' % _("TCL script file opened in Code Editor."))
except Exception as e:
log.debug("App.open_script() -> %s" % str(e))
self.inform.emit('[ERROR_NOTCL] %s' % _("Failed to open TCL Script."))
return
# Object name
script_name = outname or filename.split('/')[-1].split('\\')[-1]
# New object creation and file processing
self.on_filenewscript(name=script_name, text=script_content)
# Register recent file
self.file_opened.emit("script", filename)
# GUI feedback
self.inform.emit('[success] %s: %s' % (_("Opened"), filename))
def open_config_file(self, filename, run_from_arg=None):
"""
@ -10816,6 +10899,8 @@ class App(QtCore.QObject):
"excellon": "share/drill16.png",
'geometry': "share/geometry16.png",
"cncjob": "share/cnc16.png",
"script": "share/script_new24.png",
"document": "share/notes16_1.png",
"project": "share/project16.png",
"svg": "share/geometry16.png",
"dxf": "share/dxf16.png",
@ -10829,6 +10914,8 @@ class App(QtCore.QObject):
'excellon': lambda fname: self.worker_task.emit({'fcn': self.open_excellon, 'params': [fname]}),
'geometry': lambda fname: self.worker_task.emit({'fcn': self.import_dxf, 'params': [fname]}),
'cncjob': lambda fname: self.worker_task.emit({'fcn': self.open_gcode, 'params': [fname]}),
"script": lambda fname: self.worker_task.emit({'fcn': self.open_script, 'params': [fname]}),
"document": None,
'project': self.open_project,
'svg': self.import_svg,
'dxf': self.import_dxf,

View File

@ -16,7 +16,10 @@ from flatcamGUI.ObjectUI import *
from FlatCAMCommon import LoudDict
from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy
from camlib import *
import itertools
import tkinter as tk
import sys
import gettext
import FlatCAMTranslation as fcTranslate
@ -164,11 +167,23 @@ class FlatCAMObj(QtCore.QObject):
assert isinstance(self.ui, ObjectUI)
self.ui.name_entry.returnPressed.connect(self.on_name_activate)
self.ui.offset_button.clicked.connect(self.on_offset_button_click)
self.ui.scale_button.clicked.connect(self.on_scale_button_click)
self.ui.offsetvector_entry.returnPressed.connect(self.on_offset_button_click)
self.ui.scale_entry.returnPressed.connect(self.on_scale_button_click)
try:
# it will raise an exception for those FlatCAM objects that do not build UI with the common elements
self.ui.offset_button.clicked.connect(self.on_offset_button_click)
except (TypeError, AttributeError):
pass
try:
self.ui.scale_button.clicked.connect(self.on_scale_button_click)
except (TypeError, AttributeError):
pass
try:
self.ui.offsetvector_entry.returnPressed.connect(self.on_offset_button_click)
except (TypeError, AttributeError):
pass
try:
self.ui.scale_entry.returnPressed.connect(self.on_scale_button_click)
except (TypeError, AttributeError):
pass
# self.ui.skew_button.clicked.connect(self.on_skew_button_click)
def build_ui(self):
@ -6481,6 +6496,9 @@ class FlatCAMScript(FlatCAMObj):
FlatCAMObj.set_ui(self, ui)
FlatCAMApp.App.log.debug("FlatCAMScript.set_ui()")
assert isinstance(self.ui, ScriptObjectUI), \
"Expected a ScriptObjectUI, got %s" % type(self.ui)
self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper()
if self.units == "IN":
@ -6488,26 +6506,137 @@ class FlatCAMScript(FlatCAMObj):
else:
self.decimals = 2
# Fill form fields only on object create
self.to_form()
# Show/Hide Advanced Options
if self.app.defaults["global_app_level"] == 'b':
self.ui.level.setText(_(
'<span style="color:green;"><b>Basic</b></span>'
))
else:
self.ui.level.setText(_(
'<span style="color:red;"><b>Advanced</b></span>'
))
self.script_editor_tab = TextEditor(app=self.app)
# first clear previous text in text editor (if any)
self.script_editor_tab.code_editor.clear()
self.script_editor_tab.code_editor.setReadOnly(False)
self.script_editor_tab.buttonRun.show()
self.ui.autocomplete_cb.set_value(self.app.defaults['script_autocompleter'])
self.on_autocomplete_changed(state= self.app.defaults['script_autocompleter'])
flt = "FlatCAM Scripts (*.FlatScript);;All Files (*.*)"
self.script_editor_tab.buttonOpen.clicked.disconnect()
self.script_editor_tab.buttonOpen.clicked.connect(lambda: self.script_editor_tab.handleOpen(filt=flt))
self.script_editor_tab.buttonSave.clicked.disconnect()
self.script_editor_tab.buttonSave.clicked.connect(lambda: self.script_editor_tab.handleSaveGCode(filt=flt))
self.script_editor_tab.buttonRun.clicked.connect(self.handle_run_code)
self.script_editor_tab.handleTextChanged()
self.ui.autocomplete_cb.stateChanged.connect(self.on_autocomplete_changed)
# add the source file to the Code Editor
for line in self.source_file.splitlines():
self.script_editor_tab.code_editor.append(line)
self.build_ui()
def build_ui(self):
pass
FlatCAMObj.build_ui(self)
tab_here = False
# try to not add too many times a tab that it is already installed
for idx in range(self.app.ui.plot_tab_area.count()):
if self.app.ui.plot_tab_area.widget(idx).objectName() == self.options['name']:
tab_here = True
break
# add the tab if it is not already added
if tab_here is False:
self.app.ui.plot_tab_area.addTab(self.script_editor_tab, '%s' % _("Script Editor"))
self.script_editor_tab.setObjectName(self.options['name'])
# Switch plot_area to CNCJob tab
self.app.ui.plot_tab_area.setCurrentWidget(self.script_editor_tab)
def handle_run_code(self):
# trying to run a Tcl command without having the Shell open will create some warnings because the Tcl Shell
# tries to print on a hidden widget, therefore show the dock if hidden
if self.app.ui.shell_dock.isHidden():
self.app.ui.shell_dock.show()
self.script_code = deepcopy(self.script_editor_tab.code_editor.toPlainText())
old_line = ''
for tcl_command_line in self.script_code.splitlines():
# do not process lines starting with '#' = comment and empty lines
if not tcl_command_line.startswith('#') and tcl_command_line != '':
# id FlatCAM is run in Windows then replace all the slashes with
# the UNIX style slash that TCL understands
if sys.platform == 'win32':
if "open" in tcl_command_line:
tcl_command_line = tcl_command_line.replace('\\', '/')
if old_line != '':
new_command = old_line + tcl_command_line + '\n'
else:
new_command = tcl_command_line
# execute the actual Tcl command
try:
self.app.shell.open_proccessing() # Disables input box.
result = self.app.tcl.eval(str(new_command))
if result != 'None':
self.app.shell.append_output(result + '\n')
old_line = ''
except tk.TclError:
old_line = old_line + tcl_command_line + '\n'
except Exception as e:
log.debug("App.handleRunCode() --> %s" % str(e))
if old_line != '':
# it means that the script finished with an error
result = self.app.tcl.eval("set errorInfo")
log.error("Exec command Exception: %s" % (result + '\n'))
self.app.shell.append_error('ERROR: ' + result + '\n')
self.app.shell.close_proccessing()
def on_autocomplete_changed(self, state):
if state:
self.script_editor_tab.code_editor.completer_enable = True
else:
self.script_editor_tab.code_editor.completer_enable = False
class FlatCAMNotes(FlatCAMObj):
class FlatCAMDocument(FlatCAMObj):
"""
Represents a Notes object.
Represents a Document object.
"""
optionChanged = QtCore.pyqtSignal(str)
ui_type = NotesObjectUI
ui_type = DocumentObjectUI
def __init__(self, name):
FlatCAMApp.App.log.debug("Creating a Notes object...")
FlatCAMApp.App.log.debug("Creating a Document object...")
FlatCAMObj.__init__(self, name)
self.kind = "notes"
self.kind = "document"
def set_ui(self, ui):
FlatCAMObj.set_ui(self, ui)
FlatCAMApp.App.log.debug("FlatCAMNotes.set_ui()")
FlatCAMApp.App.log.debug("FlatCAMDocument.set_ui()")
assert isinstance(self.ui, DocumentObjectUI), \
"Expected a DocumentObjectUI, got %s" % type(self.ui)
self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper()
@ -6516,7 +6645,22 @@ class FlatCAMNotes(FlatCAMObj):
else:
self.decimals = 2
# Fill form fields only on object create
self.to_form()
# Show/Hide Advanced Options
if self.app.defaults["global_app_level"] == 'b':
self.ui.level.setText(_(
'<span style="color:green;"><b>Basic</b></span>'
))
else:
self.ui.level.setText(_(
'<span style="color:red;"><b>Advanced</b></span>'
))
self.build_ui()
def build_ui(self):
pass
FlatCAMObj.build_ui(self)
# end of file

View File

@ -188,7 +188,7 @@ class ObjectCollection(QtCore.QAbstractItemModel):
("geometry", "Geometry"),
("cncjob", "CNC Job"),
("script", "Scripts"),
("notes", "Notes"),
("document", "Document"),
]
classdict = {
@ -197,7 +197,7 @@ class ObjectCollection(QtCore.QAbstractItemModel):
"cncjob": FlatCAMCNCjob,
"geometry": FlatCAMGeometry,
"script": FlatCAMScript,
"notes": FlatCAMNotes
"document": FlatCAMDocument
}
icon_files = {
@ -206,7 +206,7 @@ class ObjectCollection(QtCore.QAbstractItemModel):
"cncjob": "share/cnc16.png",
"geometry": "share/geometry16.png",
"script": "share/script_new16.png",
"notes": "share/notes16_1.png"
"document": "share/notes16_1.png"
}
root_item = None
@ -328,6 +328,14 @@ class ObjectCollection(QtCore.QAbstractItemModel):
self.app.ui.menuprojectedit.setVisible(False)
if type(obj) != FlatCAMGerber and type(obj) != FlatCAMExcellon and type(obj) != FlatCAMCNCjob:
self.app.ui.menuprojectviewsource.setVisible(False)
if type(obj) != FlatCAMGerber and type(obj) != FlatCAMGeometry and type(obj) != FlatCAMExcellon and \
type(obj) != FlatCAMCNCjob:
# meaning for Scripts and for Document type of FlatCAM object
self.app.ui.menuprojectenable.setVisible(False)
self.app.ui.menuprojectdisable.setVisible(False)
self.app.ui.menuprojectedit.setVisible(False)
self.app.ui.menuprojectproperties.setVisible(False)
self.app.ui.menuprojectgeneratecnc.setVisible(False)
else:
self.app.ui.menuprojectgeneratecnc.setVisible(False)
@ -576,12 +584,19 @@ class ObjectCollection(QtCore.QAbstractItemModel):
# send signal with the object that is deleted
# self.app.object_status_changed.emit(active.obj, 'delete')
# some objects add a Tab on creation, close it here
for idx in range(self.app.ui.plot_tab_area.count()):
if self.app.ui.plot_tab_area.widget(idx).objectName() == active.obj.options['name']:
self.app.ui.plot_tab_area.removeTab(idx)
break
# update the SHELL auto-completer model data
name = active.obj.options['name']
try:
self.app.myKeywords.remove(name)
self.app.shell._edit.set_model_data(self.app.myKeywords)
self.app.ui.code_editor.set_model_data(self.app.myKeywords)
# this is not needed any more because now the code editor is created on demand
# self.app.ui.code_editor.set_model_data(self.app.myKeywords)
except Exception as e:
log.debug(
"delete_active() --> Could not remove the old object name from auto-completer model list. %s" % str(e))
@ -742,7 +757,12 @@ class ObjectCollection(QtCore.QAbstractItemModel):
elif obj.kind == 'geometry':
self.app.inform.emit(_('[selected]<span style="color:{color};">{name}</span> selected').format(
color='red', name=str(obj.options['name'])))
elif obj.kind == 'script':
self.app.inform.emit(_('[selected]<span style="color:{color};">{name}</span> selected').format(
color='orange', name=str(obj.options['name'])))
elif obj.kind == 'document':
self.app.inform.emit(_('[selected]<span style="color:{color};">{name}</span> selected').format(
color='violet', name=str(obj.options['name'])))
except IndexError:
# FlatCAMApp.App.log.debug("on_list_selection_change(): Index Error (Nothing selected?)")
self.app.inform.emit('')

View File

@ -15,6 +15,12 @@ CAD program, and create G-Code for Isolation routing.
- fixed bug in Geometry Editor that did not allow the copy of geometric elements
- created a new class that holds all the Code Editor functionality and integrated as a Editor in FlatCAM, the location is in flatcamEditors folder
- remade all the functions for view_source, scripts and view_code to use the new TextEditor class; now all the Code Editor tabs are being kept alive, before only one could be in an open state
- changed the name of the new object FlatCAMNotes to a more general one FlatCAMDocument
- changed the way a new FlatCAMScript object is made, the method that is processing the Tcl commands when the Run button is clicked is moved to the FlatCAMObj.FlatCAMScript() class
- reused the Multiprocessing Pool declared in the App for the ToolRulesCheck() class
- adapted the Project context menu for the new types of FLatCAM objects
- modified the setup_recent_files to accommodate the new FlatCAM objects
- made sure that when an FlatCAM script object is deleted, it's associated Tab is closed
1.10.2019

View File

@ -1,11 +1,6 @@
from flatcamGUI.GUIElements import *
from PyQt5 import QtPrintSupport
import tkinter as tk
from copy import deepcopy
import sys
import gettext
import FlatCAMTranslation as fcTranslate
import builtins
@ -39,10 +34,10 @@ class TextEditor(QtWidgets.QWidget):
self.code_editor = FCTextAreaExtended()
stylesheet = """
QTextEdit { selection-background-color:yellow;
selection-color:black;
}
"""
QTextEdit { selection-background-color:yellow;
selection-color:black;
}
"""
self.code_editor.setStyleSheet(stylesheet)
@ -129,7 +124,6 @@ class TextEditor(QtWidgets.QWidget):
self.code_editor.set_model_data(self.app.myKeywords)
self.gcode_edited = ''
self.script_code = ''
def handlePrint(self):
self.app.report_usage("handlePrint()")
@ -269,79 +263,38 @@ class TextEditor(QtWidgets.QWidget):
self.app.clipboard.setText(text)
self.app.inform.emit(_("Code Editor content copied to clipboard ..."))
def handleRunCode(self):
# trying to run a Tcl command without having the Shell open will create some warnings because the Tcl Shell
# tries to print on a hidden widget, therefore show the dock if hidden
if self.app.ui.shell_dock.isHidden():
self.app.ui.shell_dock.show()
self.script_code = deepcopy(self.code_editor.toPlainText())
old_line = ''
for tcl_command_line in self.app.script_code.splitlines():
# do not process lines starting with '#' = comment and empty lines
if not tcl_command_line.startswith('#') and tcl_command_line != '':
# id FlatCAM is run in Windows then replace all the slashes with
# the UNIX style slash that TCL understands
if sys.platform == 'win32':
if "open" in tcl_command_line:
tcl_command_line = tcl_command_line.replace('\\', '/')
if old_line != '':
new_command = old_line + tcl_command_line + '\n'
else:
new_command = tcl_command_line
# execute the actual Tcl command
try:
self.app.shell.open_proccessing() # Disables input box.
result = self.app.tcl.eval(str(new_command))
if result != 'None':
self.app.shell.append_output(result + '\n')
old_line = ''
except tk.TclError:
old_line = old_line + tcl_command_line + '\n'
except Exception as e:
log.debug("App.handleRunCode() --> %s" % str(e))
if old_line != '':
# it means that the script finished with an error
result = self.app.tcl.eval("set errorInfo")
log.error("Exec command Exception: %s" % (result + '\n'))
self.app.shell.append_error('ERROR: ' + result + '\n')
self.app.shell.close_proccessing()
def closeEvent(self, QCloseEvent):
try:
self.code_editor.textChanged.disconnect()
except TypeError:
pass
try:
self.buttonOpen.clicked.disconnect()
except TypeError:
pass
try:
self.buttonPrint.clicked.disconnect()
except TypeError:
pass
try:
self.buttonPreview.clicked.disconnect()
except TypeError:
pass
try:
self.buttonFind.clicked.disconnect()
except TypeError:
pass
try:
self.buttonReplace.clicked.disconnect()
except TypeError:
pass
try:
self.button_copy_all.clicked.disconnect()
except TypeError:
pass
super().closeEvent(QCloseEvent)
# def closeEvent(self, QCloseEvent):
# try:
# self.code_editor.textChanged.disconnect()
# except TypeError:
# pass
# try:
# self.buttonOpen.clicked.disconnect()
# except TypeError:
# pass
# try:
# self.buttonPrint.clicked.disconnect()
# except TypeError:
# pass
# try:
# self.buttonPreview.clicked.disconnect()
# except TypeError:
# pass
# try:
# self.buttonFind.clicked.disconnect()
# except TypeError:
# pass
# try:
# self.buttonReplace.clicked.disconnect()
# except TypeError:
# pass
# try:
# self.button_copy_all.clicked.disconnect()
# except TypeError:
# pass
# try:
# self.buttonRun.clicked.disconnect()
# except TypeError:
# pass
#
# super().closeEvent(QCloseEvent)

View File

@ -30,7 +30,7 @@ class ObjectUI(QtWidgets.QWidget):
put UI elements in ObjectUI.custom_box (QtWidgets.QLayout).
"""
def __init__(self, icon_file='share/flatcam_icon32.png', title=_('FlatCAM Object'), parent=None):
def __init__(self, icon_file='share/flatcam_icon32.png', title=_('FlatCAM Object'), parent=None, common=True):
QtWidgets.QWidget.__init__(self, parent=parent)
layout = QtWidgets.QVBoxLayout()
@ -74,62 +74,62 @@ class ObjectUI(QtWidgets.QWidget):
# ###########################
# ## Common to all objects ##
# ###########################
if common is True:
# ### Scale ####
self.scale_label = QtWidgets.QLabel('<b>%s:</b>' % _('Scale'))
self.scale_label.setToolTip(
_("Change the size of the object.")
)
layout.addWidget(self.scale_label)
# ### Scale ####
self.scale_label = QtWidgets.QLabel('<b>%s:</b>' % _('Scale'))
self.scale_label.setToolTip(
_("Change the size of the object.")
)
layout.addWidget(self.scale_label)
self.scale_grid = QtWidgets.QGridLayout()
layout.addLayout(self.scale_grid)
self.scale_grid = QtWidgets.QGridLayout()
layout.addLayout(self.scale_grid)
# Factor
faclabel = QtWidgets.QLabel('%s:' % _('Factor'))
faclabel.setToolTip(
_("Factor by which to multiply\n"
"geometric features of this object.")
)
self.scale_grid.addWidget(faclabel, 0, 0)
self.scale_entry = FloatEntry2()
self.scale_entry.set_value(1.0)
self.scale_grid.addWidget(self.scale_entry, 0, 1)
# Factor
faclabel = QtWidgets.QLabel('%s:' % _('Factor'))
faclabel.setToolTip(
_("Factor by which to multiply\n"
"geometric features of this object.")
)
self.scale_grid.addWidget(faclabel, 0, 0)
self.scale_entry = FloatEntry2()
self.scale_entry.set_value(1.0)
self.scale_grid.addWidget(self.scale_entry, 0, 1)
# GO Button
self.scale_button = QtWidgets.QPushButton(_('Scale'))
self.scale_button.setToolTip(
_("Perform scaling operation.")
)
self.scale_button.setMinimumWidth(70)
self.scale_grid.addWidget(self.scale_button, 0, 2)
# GO Button
self.scale_button = QtWidgets.QPushButton(_('Scale'))
self.scale_button.setToolTip(
_("Perform scaling operation.")
)
self.scale_button.setMinimumWidth(70)
self.scale_grid.addWidget(self.scale_button, 0, 2)
# ### Offset ####
self.offset_label = QtWidgets.QLabel('<b>%s:</b>' % _('Offset'))
self.offset_label.setToolTip(
_("Change the position of this object.")
)
layout.addWidget(self.offset_label)
# ### Offset ####
self.offset_label = QtWidgets.QLabel('<b>%s:</b>' % _('Offset'))
self.offset_label.setToolTip(
_("Change the position of this object.")
)
layout.addWidget(self.offset_label)
self.offset_grid = QtWidgets.QGridLayout()
layout.addLayout(self.offset_grid)
self.offset_grid = QtWidgets.QGridLayout()
layout.addLayout(self.offset_grid)
self.offset_vectorlabel = QtWidgets.QLabel('%s:' % _('Vector'))
self.offset_vectorlabel.setToolTip(
_("Amount by which to move the object\n"
"in the x and y axes in (x, y) format.")
)
self.offset_grid.addWidget(self.offset_vectorlabel, 0, 0)
self.offsetvector_entry = EvalEntry2()
self.offsetvector_entry.setText("(0.0, 0.0)")
self.offset_grid.addWidget(self.offsetvector_entry, 0, 1)
self.offset_vectorlabel = QtWidgets.QLabel('%s:' % _('Vector'))
self.offset_vectorlabel.setToolTip(
_("Amount by which to move the object\n"
"in the x and y axes in (x, y) format.")
)
self.offset_grid.addWidget(self.offset_vectorlabel, 0, 0)
self.offsetvector_entry = EvalEntry2()
self.offsetvector_entry.setText("(0.0, 0.0)")
self.offset_grid.addWidget(self.offsetvector_entry, 0, 1)
self.offset_button = QtWidgets.QPushButton(_('Offset'))
self.offset_button.setToolTip(
_("Perform the offset operation.")
)
self.offset_button.setMinimumWidth(70)
self.offset_grid.addWidget(self.offset_button, 0, 2)
self.offset_button = QtWidgets.QPushButton(_('Offset'))
self.offset_button.setToolTip(
_("Perform the offset operation.")
)
self.offset_button.setMinimumWidth(70)
self.offset_grid.addWidget(self.offset_button, 0, 2)
layout.addStretch()
@ -1726,286 +1726,48 @@ class ScriptObjectUI(ObjectUI):
be placed in ``self.custom_box`` to preserve the layout.
"""
ObjectUI.__init__(self, title=_('Script Object'), icon_file='share/cnc32.png', parent=parent)
# Scale and offset ans skew are not available for CNCJob objects.
# Hiding from the GUI.
for i in range(0, self.scale_grid.count()):
self.scale_grid.itemAt(i).widget().hide()
self.scale_label.hide()
self.scale_button.hide()
for i in range(0, self.offset_grid.count()):
self.offset_grid.itemAt(i).widget().hide()
self.offset_label.hide()
self.offset_button.hide()
# ## Plot options
self.plot_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("Plot Options"))
self.custom_box.addWidget(self.plot_options_label)
self.cncplot_method_label = QtWidgets.QLabel("<b>%s:</b>" % _("Plot kind"))
self.cncplot_method_label.setToolTip(
_(
"This selects the kind of geometries on the canvas to plot.\n"
"Those can be either of type 'Travel' which means the moves\n"
"above the work piece or it can be of type 'Cut',\n"
"which means the moves that cut into the material."
)
)
self.cncplot_method_combo = RadioSet([
{"label": _("All"), "value": "all"},
{"label": _("Travel"), "value": "travel"},
{"label": _("Cut"), "value": "cut"}
], stretch=False)
self.annotation_label = QtWidgets.QLabel("<b>%s:</b>" % _("Display Annotation"))
self.annotation_label.setToolTip(
_("This selects if to display text annotation on the plot.\n"
"When checked it will display numbers in order for each end\n"
"of a travel line.")
)
self.annotation_cb = FCCheckBox()
ObjectUI.__init__(self, title=_('Script Object'),
icon_file='share/script_new24.png',
parent=parent,
common=False)
# ## Object name
self.name_hlay = QtWidgets.QHBoxLayout()
self.custom_box.addLayout(self.name_hlay)
name_label = QtWidgets.QLabel("<b>%s:</b>" % _("Name"))
self.name_entry = FCEntry()
self.name_entry.setFocusPolicy(QtCore.Qt.StrongFocus)
self.name_hlay.addWidget(name_label)
self.name_hlay.addWidget(self.name_entry)
self.t_distance_label = QtWidgets.QLabel("<b>%s:</b>" % _("Travelled dist."))
self.t_distance_label.setToolTip(
_("This is the total travelled distance on X-Y plane.\n"
"In current units.")
)
self.t_distance_entry = FCEntry()
self.t_distance_entry.setToolTip(
_("This is the total travelled distance on X-Y plane.\n"
"In current units.")
)
self.units_label = QtWidgets.QLabel()
self.t_time_label = QtWidgets.QLabel("<b>%s:</b>" % _("Estimated time"))
self.t_time_label.setToolTip(
_("This is the estimated time to do the routing/drilling,\n"
"without the time spent in ToolChange events.")
)
self.t_time_entry = FCEntry()
self.t_time_entry.setToolTip(
_("This is the estimated time to do the routing/drilling,\n"
"without the time spent in ToolChange events.")
)
self.units_time_label = QtWidgets.QLabel()
f_lay = QtWidgets.QGridLayout()
f_lay.setColumnStretch(1, 1)
f_lay.setColumnStretch(2, 1)
self.custom_box.addLayout(f_lay)
f_lay.addWidget(self.cncplot_method_label, 0, 0)
f_lay.addWidget(self.cncplot_method_combo, 0, 1)
f_lay.addWidget(QtWidgets.QLabel(''), 0, 2)
f_lay.addWidget(self.annotation_label, 1, 0)
f_lay.addWidget(self.annotation_cb, 1, 1)
f_lay.addWidget(QtWidgets.QLabel(''), 1, 2)
f_lay.addWidget(self.t_distance_label, 2, 0)
f_lay.addWidget(self.t_distance_entry, 2, 1)
f_lay.addWidget(self.units_label, 2, 2)
f_lay.addWidget(self.t_time_label, 3, 0)
f_lay.addWidget(self.t_time_entry, 3, 1)
f_lay.addWidget(self.units_time_label, 3, 2)
self.t_distance_label.hide()
self.t_distance_entry.setVisible(False)
self.t_time_label.hide()
self.t_time_entry.setVisible(False)
e1_lbl = QtWidgets.QLabel('')
self.custom_box.addWidget(e1_lbl)
hlay = QtWidgets.QHBoxLayout()
self.custom_box.addLayout(hlay)
# CNC Tools Table for plot
self.cnc_tools_table_label = QtWidgets.QLabel('<b>%s</b>' % _('CNC Tools Table'))
self.cnc_tools_table_label.setToolTip(
_(
"Tools in this CNCJob object used for cutting.\n"
"The tool diameter is used for plotting on canvas.\n"
"The 'Offset' entry will set an offset for the cut.\n"
"'Offset' can be inside, outside, on path (none) and custom.\n"
"'Type' entry is only informative and it allow to know the \n"
"intent of using the current tool. \n"
"It can be Rough(ing), Finish(ing) or Iso(lation).\n"
"The 'Tool type'(TT) can be circular with 1 to 4 teeths(C1..C4),\n"
"ball(B), or V-Shaped(V)."
)
)
hlay.addWidget(self.cnc_tools_table_label)
# Plot CB
# self.plot_cb = QtWidgets.QCheckBox('Plot')
self.plot_cb = FCCheckBox(_('Plot Object'))
self.plot_cb.setToolTip(
_("Plot (show) this object.")
)
self.plot_cb.setLayoutDirection(QtCore.Qt.RightToLeft)
hlay.addStretch()
hlay.addWidget(self.plot_cb)
self.cnc_tools_table = FCTable()
self.custom_box.addWidget(self.cnc_tools_table)
# self.cnc_tools_table.setColumnCount(4)
# self.cnc_tools_table.setHorizontalHeaderLabels(['#', 'Dia', 'Plot', ''])
# self.cnc_tools_table.setColumnHidden(3, True)
self.cnc_tools_table.setColumnCount(7)
self.cnc_tools_table.setColumnWidth(0, 20)
self.cnc_tools_table.setHorizontalHeaderLabels(['#', _('Dia'), _('Offset'), _('Type'), _('TT'), '',
_('P')])
self.cnc_tools_table.setColumnHidden(5, True)
# stylesheet = "::section{Background-color:rgb(239,239,245)}"
# self.cnc_tools_table.horizontalHeader().setStyleSheet(stylesheet)
# Update plot button
self.updateplot_button = QtWidgets.QPushButton(_('Update Plot'))
self.updateplot_button.setToolTip(
_("Update the plot.")
)
self.custom_box.addWidget(self.updateplot_button)
# ####################
# ## Export G-Code ##
# ####################
self.export_gcode_label = QtWidgets.QLabel("<b>%s:</b>" % _("Export CNC Code"))
self.export_gcode_label.setToolTip(
_("Export and save G-Code to\n"
"make this object to a file.")
)
self.custom_box.addWidget(self.export_gcode_label)
# Prepend text to GCode
prependlabel = QtWidgets.QLabel('%s:' % _('Prepend to CNC Code'))
prependlabel.setToolTip(
_("Type here any G-Code commands you would\n"
"like to add at the beginning of the G-Code file.")
)
self.custom_box.addWidget(prependlabel)
self.prepend_text = FCTextArea()
self.custom_box.addWidget(self.prepend_text)
# Append text to GCode
appendlabel = QtWidgets.QLabel('%s:' % _('Append to CNC Code'))
appendlabel.setToolTip(
_("Type here any G-Code commands you would\n"
"like to append to the generated file.\n"
"I.e.: M2 (End of program)")
)
self.custom_box.addWidget(appendlabel)
self.append_text = FCTextArea()
self.custom_box.addWidget(self.append_text)
self.cnc_frame = QtWidgets.QFrame()
self.cnc_frame.setContentsMargins(0, 0, 0, 0)
self.custom_box.addWidget(self.cnc_frame)
self.cnc_box = QtWidgets.QVBoxLayout()
self.cnc_box.setContentsMargins(0, 0, 0, 0)
self.cnc_frame.setLayout(self.cnc_box)
# Toolchange Custom G-Code
self.toolchangelabel = QtWidgets.QLabel('%s:' % _('Toolchange G-Code'))
self.toolchangelabel.setToolTip(
_(
"Type here any G-Code commands you would\n"
"like to be executed when Toolchange event is encountered.\n"
"This will constitute a Custom Toolchange GCode,\n"
"or a Toolchange Macro.\n"
"The FlatCAM variables are surrounded by '%' symbol.\n\n"
"WARNING: it can be used only with a postprocessor file\n"
"that has 'toolchange_custom' in it's name and this is built\n"
"having as template the 'Toolchange Custom' posprocessor file."
)
)
self.cnc_box.addWidget(self.toolchangelabel)
self.toolchange_text = FCTextArea()
self.cnc_box.addWidget(self.toolchange_text)
cnclay = QtWidgets.QHBoxLayout()
self.cnc_box.addLayout(cnclay)
# Toolchange Replacement Enable
self.toolchange_cb = FCCheckBox(label='%s' % _('Use Toolchange Macro'))
self.toolchange_cb.setToolTip(
_("Check this box if you want to use\n"
"a Custom Toolchange GCode (macro).")
)
# Variable list
self.tc_variable_combo = FCComboBox()
self.tc_variable_combo.setToolTip(
_(
"A list of the FlatCAM variables that can be used\n"
"in the Toolchange event.\n"
"They have to be surrounded by the '%' symbol"
)
)
# Populate the Combo Box
variables = [_('Parameters'), 'tool', 'tooldia', 't_drills', 'x_toolchange', 'y_toolchange', 'z_toolchange',
'z_cut', 'z_move', 'z_depthpercut', 'spindlespeed', 'dwelltime']
self.tc_variable_combo.addItems(variables)
self.tc_variable_combo.setItemData(0, _("FlatCAM CNC parameters"), Qt.ToolTipRole)
self.tc_variable_combo.setItemData(1, _("tool = tool number"), Qt.ToolTipRole)
self.tc_variable_combo.setItemData(2, _("tooldia = tool diameter"), Qt.ToolTipRole)
self.tc_variable_combo.setItemData(3, _("t_drills = for Excellon, total number of drills"), Qt.ToolTipRole)
self.tc_variable_combo.setItemData(4, _("x_toolchange = X coord for Toolchange"), Qt.ToolTipRole)
self.tc_variable_combo.setItemData(5, _("y_toolchange = Y coord for Toolchange"), Qt.ToolTipRole)
self.tc_variable_combo.setItemData(6, _("z_toolchange = Z coord for Toolchange"), Qt.ToolTipRole)
self.tc_variable_combo.setItemData(7, _("z_cut = depth where to cut"), Qt.ToolTipRole)
self.tc_variable_combo.setItemData(8, _("z_move = height where to travel"), Qt.ToolTipRole)
self.tc_variable_combo.setItemData(9, _("z_depthpercut = the step value for multidepth cut"), Qt.ToolTipRole)
self.tc_variable_combo.setItemData(10, _("spindlesspeed = the value for the spindle speed"), Qt.ToolTipRole)
self.tc_variable_combo.setItemData(11, _("dwelltime = time to dwell to allow the "
"spindle to reach it's set RPM"),
Qt.ToolTipRole)
cnclay.addWidget(self.toolchange_cb)
cnclay.addStretch()
cnclay.addWidget(self.tc_variable_combo)
self.toolch_ois = OptionalInputSection(self.toolchange_cb,
[self.toolchangelabel, self.toolchange_text, self.tc_variable_combo])
h_lay = QtWidgets.QHBoxLayout()
h_lay.setAlignment(QtCore.Qt.AlignVCenter)
self.custom_box.addLayout(h_lay)
# Edit GCode Button
self.modify_gcode_button = QtWidgets.QPushButton(_('View CNC Code'))
self.modify_gcode_button.setToolTip(
_("Opens TAB to view/modify/print G-Code\n"
"file.")
self.autocomplete_cb = FCCheckBox("%s" % _("Auto Completer"))
self.autocomplete_cb.setToolTip(
_("This selects if the auto completer is enabled in the Script Editor.")
)
# GO Button
self.export_gcode_button = QtWidgets.QPushButton(_('Save CNC Code'))
self.export_gcode_button.setToolTip(
_("Opens dialog to save G-Code\n"
"file.")
self.autocomplete_cb.setStyleSheet(
"""
QCheckBox {font-weight: bold; color: black}
"""
)
h_lay.addWidget(self.autocomplete_cb)
h_lay.addStretch()
h_lay.addWidget(self.modify_gcode_button)
h_lay.addWidget(self.export_gcode_button)
# self.custom_box.addWidget(self.export_gcode_button)
# Plot CB - this is added only for compatibility; other FlatCAM objects expect it and the mechanism is already
# established and I don't want to changed it right now
self.plot_cb = FCCheckBox()
self.plot_cb.setLayoutDirection(QtCore.Qt.RightToLeft)
self.custom_box.addWidget(self.plot_cb)
self.plot_cb.hide()
class NotesObjectUI(ObjectUI):
self.custom_box.addStretch()
class DocumentObjectUI(ObjectUI):
"""
User interface for Notes objects.
"""
@ -2016,283 +1778,28 @@ class NotesObjectUI(ObjectUI):
be placed in ``self.custom_box`` to preserve the layout.
"""
ObjectUI.__init__(self, title=_('Notes Object'), icon_file='share/cnc32.png', parent=parent)
# Scale and offset ans skew are not available for CNCJob objects.
# Hiding from the GUI.
for i in range(0, self.scale_grid.count()):
self.scale_grid.itemAt(i).widget().hide()
self.scale_label.hide()
self.scale_button.hide()
for i in range(0, self.offset_grid.count()):
self.offset_grid.itemAt(i).widget().hide()
self.offset_label.hide()
self.offset_button.hide()
# ## Plot options
self.plot_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("Plot Options"))
self.custom_box.addWidget(self.plot_options_label)
self.cncplot_method_label = QtWidgets.QLabel("<b>%s:</b>" % _("Plot kind"))
self.cncplot_method_label.setToolTip(
_(
"This selects the kind of geometries on the canvas to plot.\n"
"Those can be either of type 'Travel' which means the moves\n"
"above the work piece or it can be of type 'Cut',\n"
"which means the moves that cut into the material."
)
)
self.cncplot_method_combo = RadioSet([
{"label": _("All"), "value": "all"},
{"label": _("Travel"), "value": "travel"},
{"label": _("Cut"), "value": "cut"}
], stretch=False)
self.annotation_label = QtWidgets.QLabel("<b>%s:</b>" % _("Display Annotation"))
self.annotation_label.setToolTip(
_("This selects if to display text annotation on the plot.\n"
"When checked it will display numbers in order for each end\n"
"of a travel line.")
)
self.annotation_cb = FCCheckBox()
ObjectUI.__init__(self, title=_('Document Object'),
icon_file='share/notes16_1.png',
parent=parent,
common=False)
# ## Object name
self.name_hlay = QtWidgets.QHBoxLayout()
self.custom_box.addLayout(self.name_hlay)
name_label = QtWidgets.QLabel("<b>%s:</b>" % _("Name"))
self.name_entry = FCEntry()
self.name_entry.setFocusPolicy(QtCore.Qt.StrongFocus)
self.name_hlay.addWidget(name_label)
self.name_hlay.addWidget(self.name_entry)
self.t_distance_label = QtWidgets.QLabel("<b>%s:</b>" % _("Travelled dist."))
self.t_distance_label.setToolTip(
_("This is the total travelled distance on X-Y plane.\n"
"In current units.")
)
self.t_distance_entry = FCEntry()
self.t_distance_entry.setToolTip(
_("This is the total travelled distance on X-Y plane.\n"
"In current units.")
)
self.units_label = QtWidgets.QLabel()
self.t_time_label = QtWidgets.QLabel("<b>%s:</b>" % _("Estimated time"))
self.t_time_label.setToolTip(
_("This is the estimated time to do the routing/drilling,\n"
"without the time spent in ToolChange events.")
)
self.t_time_entry = FCEntry()
self.t_time_entry.setToolTip(
_("This is the estimated time to do the routing/drilling,\n"
"without the time spent in ToolChange events.")
)
self.units_time_label = QtWidgets.QLabel()
f_lay = QtWidgets.QGridLayout()
f_lay.setColumnStretch(1, 1)
f_lay.setColumnStretch(2, 1)
self.custom_box.addLayout(f_lay)
f_lay.addWidget(self.cncplot_method_label, 0, 0)
f_lay.addWidget(self.cncplot_method_combo, 0, 1)
f_lay.addWidget(QtWidgets.QLabel(''), 0, 2)
f_lay.addWidget(self.annotation_label, 1, 0)
f_lay.addWidget(self.annotation_cb, 1, 1)
f_lay.addWidget(QtWidgets.QLabel(''), 1, 2)
f_lay.addWidget(self.t_distance_label, 2, 0)
f_lay.addWidget(self.t_distance_entry, 2, 1)
f_lay.addWidget(self.units_label, 2, 2)
f_lay.addWidget(self.t_time_label, 3, 0)
f_lay.addWidget(self.t_time_entry, 3, 1)
f_lay.addWidget(self.units_time_label, 3, 2)
self.t_distance_label.hide()
self.t_distance_entry.setVisible(False)
self.t_time_label.hide()
self.t_time_entry.setVisible(False)
e1_lbl = QtWidgets.QLabel('')
self.custom_box.addWidget(e1_lbl)
hlay = QtWidgets.QHBoxLayout()
self.custom_box.addLayout(hlay)
# CNC Tools Table for plot
self.cnc_tools_table_label = QtWidgets.QLabel('<b>%s</b>' % _('CNC Tools Table'))
self.cnc_tools_table_label.setToolTip(
_(
"Tools in this CNCJob object used for cutting.\n"
"The tool diameter is used for plotting on canvas.\n"
"The 'Offset' entry will set an offset for the cut.\n"
"'Offset' can be inside, outside, on path (none) and custom.\n"
"'Type' entry is only informative and it allow to know the \n"
"intent of using the current tool. \n"
"It can be Rough(ing), Finish(ing) or Iso(lation).\n"
"The 'Tool type'(TT) can be circular with 1 to 4 teeths(C1..C4),\n"
"ball(B), or V-Shaped(V)."
)
)
hlay.addWidget(self.cnc_tools_table_label)
# Plot CB
# self.plot_cb = QtWidgets.QCheckBox('Plot')
self.plot_cb = FCCheckBox(_('Plot Object'))
self.plot_cb.setToolTip(
_("Plot (show) this object.")
)
# Plot CB - this is added only for compatibility; other FlatCAM objects expect it and the mechanism is already
# established and I don't want to changed it right now
self.plot_cb = FCCheckBox()
self.plot_cb.setLayoutDirection(QtCore.Qt.RightToLeft)
hlay.addStretch()
hlay.addWidget(self.plot_cb)
self.custom_box.addWidget(self.plot_cb)
self.plot_cb.hide()
self.cnc_tools_table = FCTable()
self.custom_box.addWidget(self.cnc_tools_table)
# self.cnc_tools_table.setColumnCount(4)
# self.cnc_tools_table.setHorizontalHeaderLabels(['#', 'Dia', 'Plot', ''])
# self.cnc_tools_table.setColumnHidden(3, True)
self.cnc_tools_table.setColumnCount(7)
self.cnc_tools_table.setColumnWidth(0, 20)
self.cnc_tools_table.setHorizontalHeaderLabels(['#', _('Dia'), _('Offset'), _('Type'), _('TT'), '',
_('P')])
self.cnc_tools_table.setColumnHidden(5, True)
# stylesheet = "::section{Background-color:rgb(239,239,245)}"
# self.cnc_tools_table.horizontalHeader().setStyleSheet(stylesheet)
# Update plot button
self.updateplot_button = QtWidgets.QPushButton(_('Update Plot'))
self.updateplot_button.setToolTip(
_("Update the plot.")
)
self.custom_box.addWidget(self.updateplot_button)
# ####################
# ## Export G-Code ##
# ####################
self.export_gcode_label = QtWidgets.QLabel("<b>%s:</b>" % _("Export CNC Code"))
self.export_gcode_label.setToolTip(
_("Export and save G-Code to\n"
"make this object to a file.")
)
self.custom_box.addWidget(self.export_gcode_label)
# Prepend text to GCode
prependlabel = QtWidgets.QLabel('%s:' % _('Prepend to CNC Code'))
prependlabel.setToolTip(
_("Type here any G-Code commands you would\n"
"like to add at the beginning of the G-Code file.")
)
self.custom_box.addWidget(prependlabel)
self.prepend_text = FCTextArea()
self.custom_box.addWidget(self.prepend_text)
# Append text to GCode
appendlabel = QtWidgets.QLabel('%s:' % _('Append to CNC Code'))
appendlabel.setToolTip(
_("Type here any G-Code commands you would\n"
"like to append to the generated file.\n"
"I.e.: M2 (End of program)")
)
self.custom_box.addWidget(appendlabel)
self.append_text = FCTextArea()
self.custom_box.addWidget(self.append_text)
self.cnc_frame = QtWidgets.QFrame()
self.cnc_frame.setContentsMargins(0, 0, 0, 0)
self.custom_box.addWidget(self.cnc_frame)
self.cnc_box = QtWidgets.QVBoxLayout()
self.cnc_box.setContentsMargins(0, 0, 0, 0)
self.cnc_frame.setLayout(self.cnc_box)
# Toolchange Custom G-Code
self.toolchangelabel = QtWidgets.QLabel('%s:' % _('Toolchange G-Code'))
self.toolchangelabel.setToolTip(
_(
"Type here any G-Code commands you would\n"
"like to be executed when Toolchange event is encountered.\n"
"This will constitute a Custom Toolchange GCode,\n"
"or a Toolchange Macro.\n"
"The FlatCAM variables are surrounded by '%' symbol.\n\n"
"WARNING: it can be used only with a postprocessor file\n"
"that has 'toolchange_custom' in it's name and this is built\n"
"having as template the 'Toolchange Custom' posprocessor file."
)
)
self.cnc_box.addWidget(self.toolchangelabel)
self.toolchange_text = FCTextArea()
self.cnc_box.addWidget(self.toolchange_text)
cnclay = QtWidgets.QHBoxLayout()
self.cnc_box.addLayout(cnclay)
# Toolchange Replacement Enable
self.toolchange_cb = FCCheckBox(label='%s' % _('Use Toolchange Macro'))
self.toolchange_cb.setToolTip(
_("Check this box if you want to use\n"
"a Custom Toolchange GCode (macro).")
)
# Variable list
self.tc_variable_combo = FCComboBox()
self.tc_variable_combo.setToolTip(
_(
"A list of the FlatCAM variables that can be used\n"
"in the Toolchange event.\n"
"They have to be surrounded by the '%' symbol"
)
)
# Populate the Combo Box
variables = [_('Parameters'), 'tool', 'tooldia', 't_drills', 'x_toolchange', 'y_toolchange', 'z_toolchange',
'z_cut', 'z_move', 'z_depthpercut', 'spindlespeed', 'dwelltime']
self.tc_variable_combo.addItems(variables)
self.tc_variable_combo.setItemData(0, _("FlatCAM CNC parameters"), Qt.ToolTipRole)
self.tc_variable_combo.setItemData(1, _("tool = tool number"), Qt.ToolTipRole)
self.tc_variable_combo.setItemData(2, _("tooldia = tool diameter"), Qt.ToolTipRole)
self.tc_variable_combo.setItemData(3, _("t_drills = for Excellon, total number of drills"), Qt.ToolTipRole)
self.tc_variable_combo.setItemData(4, _("x_toolchange = X coord for Toolchange"), Qt.ToolTipRole)
self.tc_variable_combo.setItemData(5, _("y_toolchange = Y coord for Toolchange"), Qt.ToolTipRole)
self.tc_variable_combo.setItemData(6, _("z_toolchange = Z coord for Toolchange"), Qt.ToolTipRole)
self.tc_variable_combo.setItemData(7, _("z_cut = depth where to cut"), Qt.ToolTipRole)
self.tc_variable_combo.setItemData(8, _("z_move = height where to travel"), Qt.ToolTipRole)
self.tc_variable_combo.setItemData(9, _("z_depthpercut = the step value for multidepth cut"), Qt.ToolTipRole)
self.tc_variable_combo.setItemData(10, _("spindlesspeed = the value for the spindle speed"), Qt.ToolTipRole)
self.tc_variable_combo.setItemData(11, _("dwelltime = time to dwell to allow the "
"spindle to reach it's set RPM"),
Qt.ToolTipRole)
cnclay.addWidget(self.toolchange_cb)
cnclay.addStretch()
cnclay.addWidget(self.tc_variable_combo)
self.toolch_ois = OptionalInputSection(self.toolchange_cb,
[self.toolchangelabel, self.toolchange_text, self.tc_variable_combo])
h_lay = QtWidgets.QHBoxLayout()
h_lay.setAlignment(QtCore.Qt.AlignVCenter)
self.custom_box.addLayout(h_lay)
# Edit GCode Button
self.modify_gcode_button = QtWidgets.QPushButton(_('View CNC Code'))
self.modify_gcode_button.setToolTip(
_("Opens TAB to view/modify/print G-Code\n"
"file.")
)
# GO Button
self.export_gcode_button = QtWidgets.QPushButton(_('Save CNC Code'))
self.export_gcode_button.setToolTip(
_("Opens dialog to save G-Code\n"
"file.")
)
h_lay.addWidget(self.modify_gcode_button)
h_lay.addWidget(self.export_gcode_button)
# self.custom_box.addWidget(self.export_gcode_button)
self.custom_box.addStretch()
# end of file

View File

@ -486,7 +486,7 @@ class RulesCheck(FlatCAMTool):
self.constrain_flag = False
# Multiprocessing Process Pool
self.pool = Pool(processes=cpu_count())
self.pool = self.app.pool
self.results = None
# def on_object_loaded(self, index, row):

View File

@ -47,7 +47,7 @@ class TclCommandCncjob(TclCommandSignaled):
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
required = ['name']
required = []
# structured help for current command, args needs to be ordered
help = {
@ -88,16 +88,24 @@ class TclCommandCncjob(TclCommandSignaled):
:return: None or exception
"""
name = args['name']
if 'outname' not in args:
args['outname'] = str(name) + "_cnc"
name = ''
if 'muted' in args:
muted = args['muted']
else:
muted = 0
try:
name = args['name']
except KeyError:
if muted == 0:
self.raise_tcl_error("Object name is missing")
else:
return "fail"
if 'outname' not in args:
args['outname'] = str(name) + "_cnc"
obj = self.app.collection.get_by_name(str(name), isCaseSensitive=False)
if obj is None: