diff --git a/FlatCAMApp.py b/FlatCAMApp.py
index 40e94743..90e110b6 100644
--- a/FlatCAMApp.py
+++ b/FlatCAMApp.py
@@ -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: {name}').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: {name}').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,
diff --git a/FlatCAMObj.py b/FlatCAMObj.py
index b54d019b..e51ca928 100644
--- a/FlatCAMObj.py
+++ b/FlatCAMObj.py
@@ -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(_(
+ 'Basic'
+ ))
+ else:
+ self.ui.level.setText(_(
+ 'Advanced'
+ ))
+
+ 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(_(
+ 'Basic'
+ ))
+ else:
+ self.ui.level.setText(_(
+ 'Advanced'
+ ))
+
+ self.build_ui()
+
def build_ui(self):
- pass
+ FlatCAMObj.build_ui(self)
# end of file
diff --git a/ObjectCollection.py b/ObjectCollection.py
index 7d5e82e5..33eccd38 100644
--- a/ObjectCollection.py
+++ b/ObjectCollection.py
@@ -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]{name} selected').format(
color='red', name=str(obj.options['name'])))
-
+ elif obj.kind == 'script':
+ self.app.inform.emit(_('[selected]{name} selected').format(
+ color='orange', name=str(obj.options['name'])))
+ elif obj.kind == 'document':
+ self.app.inform.emit(_('[selected]{name} 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('')
diff --git a/README.md b/README.md
index 56bf1810..bfc79fcb 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/flatcamEditors/FlatCAMTextEditor.py b/flatcamEditors/FlatCAMTextEditor.py
index 0829c8fa..61e41ad0 100644
--- a/flatcamEditors/FlatCAMTextEditor.py
+++ b/flatcamEditors/FlatCAMTextEditor.py
@@ -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)
diff --git a/flatcamGUI/ObjectUI.py b/flatcamGUI/ObjectUI.py
index 1a4d8605..5448b42f 100644
--- a/flatcamGUI/ObjectUI.py
+++ b/flatcamGUI/ObjectUI.py
@@ -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('%s:' % _('Scale'))
+ self.scale_label.setToolTip(
+ _("Change the size of the object.")
+ )
+ layout.addWidget(self.scale_label)
- # ### Scale ####
- self.scale_label = QtWidgets.QLabel('%s:' % _('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('%s:' % _('Offset'))
+ self.offset_label.setToolTip(
+ _("Change the position of this object.")
+ )
+ layout.addWidget(self.offset_label)
- # ### Offset ####
- self.offset_label = QtWidgets.QLabel('%s:' % _('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("%s:" % _("Plot Options"))
- self.custom_box.addWidget(self.plot_options_label)
-
- self.cncplot_method_label = QtWidgets.QLabel("%s:" % _("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("%s:" % _("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("%s:" % _("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("%s:" % _("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("%s:" % _("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('%s' % _('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("%s:" % _("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("%s:" % _("Plot Options"))
- self.custom_box.addWidget(self.plot_options_label)
-
- self.cncplot_method_label = QtWidgets.QLabel("%s:" % _("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("%s:" % _("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("%s:" % _("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("%s:" % _("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("%s:" % _("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('%s' % _('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("%s:" % _("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
diff --git a/flatcamTools/ToolRulesCheck.py b/flatcamTools/ToolRulesCheck.py
index fa5d5d0b..26f7c26d 100644
--- a/flatcamTools/ToolRulesCheck.py
+++ b/flatcamTools/ToolRulesCheck.py
@@ -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):
diff --git a/tclCommands/TclCommandCncjob.py b/tclCommands/TclCommandCncjob.py
index 28deb94b..74778c26 100644
--- a/tclCommands/TclCommandCncjob.py
+++ b/tclCommands/TclCommandCncjob.py
@@ -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: