Merge remote-tracking branch 'origin/Beta_8.994' into Beta_8.994

# Conflicts:
#	CHANGELOG.md
This commit is contained in:
Marius 2020-07-25 12:41:23 +03:00
commit e7e1d959fd
16 changed files with 687 additions and 116 deletions

View File

@ -11,6 +11,17 @@ CHANGELOG for FlatCAM beta
- Tools Tab is hidden when entering into a Editor and showed on exit (this needs to be remade such that the toolbars state should be restored to whatever it was before entering in the Editor)
22.07.2020
- working on a proper GCode Editor
- wip in the GCode Editor
- added a Laser preprocessor named 'Z_laser' which will change the Z to the Travel Z on each ToolChange event allowing therefore control of the dot size
- by default now a new blank Geometry object created by FlatCAM is of type multigeo
- made sure that optimizations of lines when importing SVG or DXF as lines will not encounter polygons but only LinesStrings or LinearRings, otherwise having crashes
- fixed the import SVG and import DXF, when importing as Geometry to be imported as multigeo tool
- fixed the import SVG and import DXF, the source files will be saved as loaded into the source_file attribute of the resulting object (be it Geometry or Gerber)
- in import SVG and import DXF methods made sure that any polygons that are imported as polygons will survive and only the lines are optimized (changed the behavior of the above made modification)
21.07.2020
- updated the FCRadio class with a method that allow disabling certain options

View File

@ -2569,7 +2569,7 @@ class AppExcEditor(QtCore.QObject):
self.set_ui()
# now that we hava data, create the appGUI interface and add it to the Tool Tab
# now that we have data, create the appGUI interface and add it to the Tool Tab
self.build_ui(first_run=True)
# we activate this after the initial build as we don't need to see the tool been populated

View File

@ -3979,77 +3979,6 @@ class AppGeoEditor(QtCore.QObject):
# self.storage = AppGeoEditor.make_storage()
self.replot()
def edit_fcgeometry(self, fcgeometry, multigeo_tool=None):
"""
Imports the geometry from the given FlatCAM Geometry object
into the editor.
:param fcgeometry: GeometryObject
:param multigeo_tool: A tool for the case of the edited geometry being of type 'multigeo'
:return: None
"""
assert isinstance(fcgeometry, Geometry), "Expected a Geometry, got %s" % type(fcgeometry)
self.deactivate()
self.activate()
self.set_ui()
# Hide original geometry
self.fcgeometry = fcgeometry
fcgeometry.visible = False
# Set selection tolerance
DrawToolShape.tolerance = fcgeometry.drawing_tolerance * 10
self.select_tool("select")
if self.app.defaults['geometry_spindledir'] == 'CW':
if self.app.defaults['geometry_editor_milling_type'] == 'cl':
milling_type = 1 # CCW motion = climb milling (spindle is rotating CW)
else:
milling_type = -1 # CW motion = conventional milling (spindle is rotating CW)
else:
if self.app.defaults['geometry_editor_milling_type'] == 'cl':
milling_type = -1 # CCW motion = climb milling (spindle is rotating CCW)
else:
milling_type = 1 # CW motion = conventional milling (spindle is rotating CCW)
# Link shapes into editor.
if multigeo_tool:
self.multigeo_tool = multigeo_tool
geo_to_edit = self.flatten(geometry=fcgeometry.tools[self.multigeo_tool]['solid_geometry'],
orient_val=milling_type)
self.app.inform.emit(
'[WARNING_NOTCL] %s: %s %s: %s' % (
_("Editing MultiGeo Geometry, tool"),
str(self.multigeo_tool),
_("with diameter"),
str(fcgeometry.tools[self.multigeo_tool]['tooldia'])
)
)
else:
geo_to_edit = self.flatten(geometry=fcgeometry.solid_geometry, orient_val=milling_type)
for shape in geo_to_edit:
if shape is not None:
if type(shape) == Polygon:
self.add_shape(DrawToolShape(shape.exterior))
for inter in shape.interiors:
self.add_shape(DrawToolShape(inter))
else:
self.add_shape(DrawToolShape(shape))
self.replot()
# updated units
self.units = self.app.defaults['units'].upper()
self.decimals = self.app.decimals
# start with GRID toolbar activated
if self.app.ui.grid_snap_btn.isChecked() is False:
self.app.ui.grid_snap_btn.trigger()
def on_buffer_tool(self):
buff_tool = BufferSelectionTool(self.app, self)
buff_tool.run()
@ -4700,6 +4629,77 @@ class AppGeoEditor(QtCore.QObject):
return snap_x, snap_y
def edit_fcgeometry(self, fcgeometry, multigeo_tool=None):
"""
Imports the geometry from the given FlatCAM Geometry object
into the editor.
:param fcgeometry: GeometryObject
:param multigeo_tool: A tool for the case of the edited geometry being of type 'multigeo'
:return: None
"""
assert isinstance(fcgeometry, Geometry), "Expected a Geometry, got %s" % type(fcgeometry)
self.deactivate()
self.activate()
self.set_ui()
# Hide original geometry
self.fcgeometry = fcgeometry
fcgeometry.visible = False
# Set selection tolerance
DrawToolShape.tolerance = fcgeometry.drawing_tolerance * 10
self.select_tool("select")
if self.app.defaults['geometry_spindledir'] == 'CW':
if self.app.defaults['geometry_editor_milling_type'] == 'cl':
milling_type = 1 # CCW motion = climb milling (spindle is rotating CW)
else:
milling_type = -1 # CW motion = conventional milling (spindle is rotating CW)
else:
if self.app.defaults['geometry_editor_milling_type'] == 'cl':
milling_type = -1 # CCW motion = climb milling (spindle is rotating CCW)
else:
milling_type = 1 # CW motion = conventional milling (spindle is rotating CCW)
# Link shapes into editor.
if multigeo_tool:
self.multigeo_tool = multigeo_tool
geo_to_edit = self.flatten(geometry=fcgeometry.tools[self.multigeo_tool]['solid_geometry'],
orient_val=milling_type)
self.app.inform.emit(
'[WARNING_NOTCL] %s: %s %s: %s' % (
_("Editing MultiGeo Geometry, tool"),
str(self.multigeo_tool),
_("with diameter"),
str(fcgeometry.tools[self.multigeo_tool]['tooldia'])
)
)
else:
geo_to_edit = self.flatten(geometry=fcgeometry.solid_geometry, orient_val=milling_type)
for shape in geo_to_edit:
if shape is not None:
if type(shape) == Polygon:
self.add_shape(DrawToolShape(shape.exterior))
for inter in shape.interiors:
self.add_shape(DrawToolShape(inter))
else:
self.add_shape(DrawToolShape(shape))
self.replot()
# updated units
self.units = self.app.defaults['units'].upper()
self.decimals = self.app.decimals
# start with GRID toolbar activated
if self.app.ui.grid_snap_btn.isChecked() is False:
self.app.ui.grid_snap_btn.trigger()
def update_fcgeometry(self, fcgeometry):
"""
Transfers the geometry tool shape buffer to the selected geometry

View File

@ -5,7 +5,7 @@
# MIT Licence #
# ##########################################################
from appGUI.GUIElements import FCFileSaveDialog, FCEntry, FCTextAreaExtended, FCTextAreaLineNumber
from appGUI.GUIElements import FCFileSaveDialog, FCEntry, FCTextAreaExtended, FCTextAreaLineNumber, FCButton
from PyQt5 import QtPrintSupport, QtWidgets, QtCore, QtGui
from reportlab.platypus import SimpleDocTemplate, Paragraph
@ -30,6 +30,7 @@ class AppTextEditor(QtWidgets.QWidget):
self.app = app
self.plain_text = plain_text
self.callback = lambda x: None
self.setSizePolicy(
QtWidgets.QSizePolicy.MinimumExpanding,
@ -71,17 +72,17 @@ class AppTextEditor(QtWidgets.QWidget):
if text:
self.code_editor.setPlainText(text)
self.buttonPreview = QtWidgets.QPushButton(_('Print Preview'))
self.buttonPreview = FCButton(_('Print Preview'))
self.buttonPreview.setIcon(QtGui.QIcon(self.app.resource_location + '/preview32.png'))
self.buttonPreview.setToolTip(_("Open a OS standard Preview Print window."))
self.buttonPreview.setMinimumWidth(100)
self.buttonPrint = QtWidgets.QPushButton(_('Print Code'))
self.buttonPrint = FCButton(_('Print Code'))
self.buttonPrint.setIcon(QtGui.QIcon(self.app.resource_location + '/printer32.png'))
self.buttonPrint.setToolTip(_("Open a OS standard Print window."))
self.buttonPrint.setMinimumWidth(100)
self.buttonFind = QtWidgets.QPushButton(_('Find in Code'))
self.buttonFind = FCButton(_('Find in Code'))
self.buttonFind.setIcon(QtGui.QIcon(self.app.resource_location + '/find32.png'))
self.buttonFind.setToolTip(_("Will search and highlight in yellow the string in the Find box."))
self.buttonFind.setMinimumWidth(100)
@ -89,7 +90,7 @@ class AppTextEditor(QtWidgets.QWidget):
self.entryFind = FCEntry()
self.entryFind.setToolTip(_("Find box. Enter here the strings to be searched in the text."))
self.buttonReplace = QtWidgets.QPushButton(_('Replace With'))
self.buttonReplace = FCButton(_('Replace With'))
self.buttonReplace.setIcon(QtGui.QIcon(self.app.resource_location + '/replace32.png'))
self.buttonReplace.setToolTip(_("Will replace the string from the Find box with the one in the Replace box."))
self.buttonReplace.setMinimumWidth(100)
@ -101,22 +102,22 @@ class AppTextEditor(QtWidgets.QWidget):
self.sel_all_cb.setToolTip(_("When checked it will replace all instances in the 'Find' box\n"
"with the text in the 'Replace' box.."))
self.button_copy_all = QtWidgets.QPushButton(_('Copy All'))
self.button_copy_all = FCButton(_('Copy All'))
self.button_copy_all.setIcon(QtGui.QIcon(self.app.resource_location + '/copy_file32.png'))
self.button_copy_all.setToolTip(_("Will copy all the text in the Code Editor to the clipboard."))
self.button_copy_all.setMinimumWidth(100)
self.buttonOpen = QtWidgets.QPushButton(_('Open Code'))
self.buttonOpen = FCButton(_('Open Code'))
self.buttonOpen.setIcon(QtGui.QIcon(self.app.resource_location + '/folder32_bis.png'))
self.buttonOpen.setToolTip(_("Will open a text file in the editor."))
self.buttonOpen.setMinimumWidth(100)
self.buttonSave = QtWidgets.QPushButton(_('Save Code'))
self.buttonSave = FCButton(_('Save Code'))
self.buttonSave.setIcon(QtGui.QIcon(self.app.resource_location + '/save_as.png'))
self.buttonSave.setToolTip(_("Will save the text in the editor into a file."))
self.buttonSave.setMinimumWidth(100)
self.buttonRun = QtWidgets.QPushButton(_('Run Code'))
self.buttonRun = FCButton(_('Run Code'))
self.buttonRun.setToolTip(_("Will run the TCL commands found in the text file, one by one."))
self.buttonRun.setMinimumWidth(100)
@ -162,6 +163,9 @@ class AppTextEditor(QtWidgets.QWidget):
self.code_edited = ''
def set_callback(self, callback):
self.callback = callback
def handlePrint(self):
self.app.defaults.report_usage("handlePrint()")

View File

@ -0,0 +1,322 @@
# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# File Author: Marius Adrian Stanciu (c) #
# Date: 07/22/2020 #
# MIT Licence #
# ##########################################################
from appEditors.AppTextEditor import AppTextEditor
from appObjects.FlatCAMCNCJob import CNCJobObject
from appGUI.GUIElements import FCTextArea, FCEntry, FCButton
from PyQt5 import QtWidgets, QtCore, QtGui
# from io import StringIO
import logging
import gettext
import appTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
log = logging.getLogger('base')
class AppGCodeEditor(QtCore.QObject):
def __init__(self, app, parent=None):
super().__init__(parent=parent)
self.app = app
self.plain_text = ''
self.callback = lambda x: None
self.ui = AppGCodeEditorUI(app=self.app)
self.gcode_obj = None
self.code_edited = ''
def set_ui(self):
"""
:return:
:rtype:
"""
# #############################################################################################################
# ############# ADD a new TAB in the PLot Tab Area
# #############################################################################################################
self.ui.gcode_editor_tab = AppTextEditor(app=self.app, plain_text=True)
# add the tab if it was closed
self.app.ui.plot_tab_area.addTab(self.ui.gcode_editor_tab, '%s' % _("Code Editor"))
self.ui.gcode_editor_tab.setObjectName('code_editor_tab')
# delete the absolute and relative position and messages in the infobar
self.app.ui.position_label.setText("")
self.app.ui.rel_position_label.setText("")
self.ui.gcode_editor_tab.code_editor.completer_enable = False
self.ui.gcode_editor_tab.buttonRun.hide()
# Switch plot_area to CNCJob tab
self.app.ui.plot_tab_area.setCurrentWidget(self.ui.gcode_editor_tab)
self.ui.gcode_editor_tab.t_frame.hide()
self.ui.gcode_editor_tab.t_frame.show()
self.app.proc_container.view.set_idle()
# #############################################################################################################
# #############################################################################################################
self.ui.append_text.set_value(self.app.defaults["cncjob_append"])
self.ui.prepend_text.set_value(self.app.defaults["cncjob_prepend"])
# #################################################################################
# ################### SIGNALS #####################################################
# #################################################################################
self.ui.update_gcode_button.clicked.connect(self.insert_gcode)
self.ui.exit_editor_button.clicked.connect(self.update_fcgcode)
def build_ui(self):
"""
:return:
:rtype:
"""
# Remove anything else in the GUI Selected Tab
self.app.ui.selected_scroll_area.takeWidget()
# Put ourselves in the GUI Selected Tab
self.app.ui.selected_scroll_area.setWidget(self.ui.edit_widget)
# Switch notebook to Selected page
self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
def ui_connect(self):
"""
:return:
:rtype:
"""
pass
def ui_disconnect(self):
"""
:return:
:rtype:
"""
pass
def handleTextChanged(self):
"""
:return:
:rtype:
"""
# enable = not self.ui.code_editor.document().isEmpty()
# self.ui.buttonPrint.setEnabled(enable)
# self.ui.buttonPreview.setEnabled(enable)
self.buttonSave.setStyleSheet("QPushButton {color: red;}")
self.buttonSave.setIcon(QtGui.QIcon(self.app.resource_location + '/save_as_red.png'))
def insert_gcode(self):
"""
:return:
:rtype:
"""
pass
def edit_fcgcode(self, cnc_obj):
"""
:param cnc_obj:
:type cnc_obj:
:return:
:rtype:
"""
assert isinstance(cnc_obj, CNCJobObject)
self.gcode_obj = cnc_obj
gcode_text = self.gcode_obj.source_file
self.set_ui()
self.build_ui()
# then append the text from GCode to the text editor
self.ui.gcode_editor_tab.load_text(gcode_text, move_to_start=True, clear_text=True)
self.app.inform.emit('[success] %s...' % _('Loaded Machine Code into Code Editor'))
def update_fcgcode(self):
"""
:return:
:rtype:
"""
preamble = str(self.ui.prepend_text.get_value())
postamble = str(self.ui.append_text.get_value())
my_gcode = self.ui.gcode_editor_tab.code_editor.toPlainText()
self.gcode_obj.source_file = my_gcode
self.ui.gcode_editor_tab.buttonSave.setStyleSheet("")
self.ui.gcode_editor_tab.setIcon(QtGui.QIcon(self.app.resource_location + '/save_as.png'))
def on_open_gcode(self):
"""
:return:
:rtype:
"""
_filter_ = "G-Code Files (*.nc);; G-Code Files (*.txt);; G-Code Files (*.tap);; G-Code Files (*.cnc);; " \
"All Files (*.*)"
path, _f = QtWidgets.QFileDialog.getOpenFileName(
caption=_('Open file'), directory=self.app.get_last_folder(), filter=_filter_)
if path:
file = QtCore.QFile(path)
if file.open(QtCore.QIODevice.ReadOnly):
stream = QtCore.QTextStream(file)
self.code_edited = stream.readAll()
self.ui.gcode_editor_tab.load_text(self.code_edited, move_to_start=True, clear_text=True)
file.close()
class AppGCodeEditorUI:
def __init__(self, app):
self.app = app
# Number of decimals used by tools in this class
self.decimals = self.app.decimals
# ## Current application units in Upper Case
self.units = self.app.defaults['units'].upper()
# self.setSizePolicy(
# QtWidgets.QSizePolicy.MinimumExpanding,
# QtWidgets.QSizePolicy.MinimumExpanding
# )
self.gcode_editor_tab = None
self.edit_widget = QtWidgets.QWidget()
# ## Box for custom widgets
# This gets populated in offspring implementations.
layout = QtWidgets.QVBoxLayout()
self.edit_widget.setLayout(layout)
# add a frame and inside add a vertical box layout. Inside this vbox layout I add all the Drills widgets
# this way I can hide/show the frame
self.edit_frame = QtWidgets.QFrame()
self.edit_frame.setContentsMargins(0, 0, 0, 0)
layout.addWidget(self.edit_frame)
self.edit_box = QtWidgets.QVBoxLayout()
self.edit_box.setContentsMargins(0, 0, 0, 0)
self.edit_frame.setLayout(self.edit_box)
# ## Page Title box (spacing between children)
self.title_box = QtWidgets.QHBoxLayout()
self.edit_box.addLayout(self.title_box)
# ## Page Title icon
pixmap = QtGui.QPixmap(self.app.resource_location + '/flatcam_icon32.png')
self.icon = QtWidgets.QLabel()
self.icon.setPixmap(pixmap)
self.title_box.addWidget(self.icon, stretch=0)
# ## Title label
self.title_label = QtWidgets.QLabel("<font size=5><b>%s</b></font>" % _('GCode Editor'))
self.title_label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
self.title_box.addWidget(self.title_label, stretch=1)
# ## Object name
self.name_box = QtWidgets.QHBoxLayout()
self.edit_box.addLayout(self.name_box)
name_label = QtWidgets.QLabel(_("Name:"))
self.name_box.addWidget(name_label)
self.name_entry = FCEntry()
self.name_box.addWidget(self.name_entry)
# 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.edit_box.addWidget(prependlabel)
self.prepend_text = FCTextArea()
self.prepend_text.setPlaceholderText(
_("Type here any G-Code commands you would\n"
"like to add at the beginning of the G-Code file.")
)
self.edit_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.edit_box.addWidget(appendlabel)
self.append_text = FCTextArea()
self.append_text.setPlaceholderText(
_("Type here any G-Code commands you would\n"
"like to append to the generated file.\n"
"I.e.: M2 (End of program)")
)
self.edit_box.addWidget(self.append_text)
h_lay = QtWidgets.QHBoxLayout()
h_lay.setAlignment(QtCore.Qt.AlignVCenter)
self.edit_box.addLayout(h_lay)
# GO Button
self.update_gcode_button = FCButton(_('Update Code'))
# self.update_gcode_button.setIcon(QtGui.QIcon(self.app.resource_location + '/save_as.png'))
self.update_gcode_button.setToolTip(
_("Update the Gcode in the Editor with the values\n"
"in the 'Prepend' and 'Append' text boxes.")
)
h_lay.addWidget(self.update_gcode_button)
layout.addStretch()
# Editor
self.exit_editor_button = FCButton(_('Exit Editor'))
self.exit_editor_button.setIcon(QtGui.QIcon(self.app.resource_location + '/power16.png'))
self.exit_editor_button.setToolTip(
_("Exit from Editor.")
)
self.exit_editor_button.setStyleSheet("""
QPushButton
{
font-weight: bold;
}
""")
layout.addWidget(self.exit_editor_button)
# ############################ FINSIHED GUI ##################################################################
# #############################################################################################################
def confirmation_message(self, accepted, minval, maxval):
if accepted is False:
self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' % (_("Edited value is out of range"),
self.decimals,
minval,
self.decimals,
maxval), False)
else:
self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
def confirmation_message_int(self, accepted, minval, maxval):
if accepted is False:
self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' %
(_("Edited value is out of range"), minval, maxval), False)
else:
self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)

View File

@ -1557,8 +1557,13 @@ class FCInputDialog(QtWidgets.QInputDialog):
class FCButton(QtWidgets.QPushButton):
def __init__(self, parent=None):
super(FCButton, self).__init__(parent)
def __init__(self, text=None, checkable=None, click_callback=None, parent=None):
super(FCButton, self).__init__(text, parent)
if not checkable is None:
self.setCheckable(checkable)
if not click_callback is None:
self.clicked.connect(click_callback)
def get_value(self):
return self.isChecked()

View File

@ -1870,7 +1870,7 @@ class CNCObjectUI(ObjectUI):
self.custom_box.addWidget(self.updateplot_button)
# Editor
self.editor_button = QtWidgets.QPushButton(_('GCode Editor'))
self.editor_button = FCButton(_('GCode Editor'))
self.editor_button.setIcon(QtGui.QIcon(self.app.resource_location + '/edit_file32.png'))
self.editor_button.setToolTip(

View File

@ -223,11 +223,37 @@ class AppObject(QtCore.QObject):
:return: None
"""
outname = 'new_geo'
def initialize(obj, app):
obj.multitool = False
obj.multitool = True
obj.multigeo = True
# store here the default data for Geometry Data
default_data = {}
self.new_object('geometry', 'new_geo', initialize, plot=False)
for opt_key, opt_val in app.options.items():
if opt_key.find('geometry' + "_") == 0:
oname = opt_key[len('geometry') + 1:]
default_data[oname] = self.app.options[opt_key]
if opt_key.find('tools_mill' + "_") == 0:
oname = opt_key[len('tools_mill') + 1:]
default_data[oname] = self.app.options[opt_key]
obj.tools = {}
obj.tools.update({
1: {
'tooldia': float(app.defaults["geometry_cnctooldia"]),
'offset': 'Path',
'offset_value': 0.0,
'type': _('Rough'),
'tool_type': 'C1',
'data': deepcopy(default_data),
'solid_geometry': []
}
})
obj.tools[1]['data']['name'] = outname
self.new_object('geometry', outname, initialize, plot=False)
def new_gerber_object(self):
"""

View File

@ -149,6 +149,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
self.gcode_editor_tab = None
self.source_file = ''
self.units_found = self.app.defaults['units']
def build_ui(self):
@ -538,10 +539,14 @@ class CNCJobObject(FlatCAMObj, CNCjob):
self.ui.name_entry.set_value(new_name)
self.on_name_activate(silent=True)
preamble = str(self.ui.prepend_text.get_value())
postamble = str(self.ui.append_text.get_value())
try:
preamble = str(self.ui.prepend_text.get_value())
postamble = str(self.ui.append_text.get_value())
gc = self.export_gcode(filename, preamble=preamble, postamble=postamble)
except Exception as err:
log.debug("CNCJobObject.export_gcode_handler() --> %s" % str(err))
gc = self.export_gcode(filename)
gc = self.export_gcode(filename, preamble=preamble, postamble=postamble)
if gc == 'fail':
return
@ -590,15 +595,19 @@ class CNCJobObject(FlatCAMObj, CNCjob):
try:
self.gcode_editor_tab.load_text(self.app.gcode_edited.getvalue(), move_to_start=True, clear_text=True)
except Exception as e:
log.debug('FlatCAMCNNJob.on_edit_code_click() -->%s' % str(e))
self.app.inform.emit('[ERROR] %s %s' % ('FlatCAMCNNJob.on_edit_code_click() -->', str(e)))
log.debug('FlatCAMCNCJob.on_edit_code_click() -->%s' % str(e))
return
self.gcode_editor_tab.t_frame.show()
self.app.proc_container.view.set_idle()
self.gcode_editor_tab.buttonSave.clicked.connect(self.on_update_source_file)
self.app.inform.emit('[success] %s...' % _('Loaded Machine Code into Code Editor'))
def on_update_source_file(self):
self.source_file = self.gcode_editor_tab.code_editor.toPlainText()
def gcode_header(self, comment_start_symbol=None, comment_stop_symbol=None):
"""
Will create a header to be added to all GCode files generated by FlatCAM
@ -735,6 +744,11 @@ class CNCJobObject(FlatCAMObj, CNCjob):
include_header = True
if preamble == '':
preamble = self.app.defaults["cncjob_prepend"]
if postamble == '':
preamble = self.app.defaults["cncjob_append"]
try:
if self.special_group:
self.app.inform.emit('[WARNING_NOTCL] %s %s %s.' %

View File

@ -1881,6 +1881,7 @@ class GeometryObject(FlatCAMObj, Geometry):
job_obj.z_pdepth = float(self.app.defaults["geometry_z_pdepth"])
job_obj.feedrate_probe = float(self.app.defaults["geometry_feedrate_probe"])
total_gcode = ''
for tooluid_key in list(tools_dict.keys()):
tool_cnt += 1
@ -1970,6 +1971,8 @@ class GeometryObject(FlatCAMObj, Geometry):
else:
dia_cnc_dict['gcode'] = res
total_gcode += res
# tell gcode_parse from which point to start drawing the lines depending on what kind of
# object is the source of gcode
job_obj.toolchange_xy_type = "geometry"
@ -1993,6 +1996,8 @@ class GeometryObject(FlatCAMObj, Geometry):
})
dia_cnc_dict.clear()
job_obj.source_file = total_gcode
# Object initialization function for app.app_obj.new_object()
# RUNNING ON SEPARATE THREAD!
def job_init_multi_geometry(job_obj, app_obj):
@ -2031,6 +2036,7 @@ class GeometryObject(FlatCAMObj, Geometry):
self.app.inform.emit('[ERROR_NOTCL] %s...' % _('Cancelled. Empty file, it has no geometry'))
return 'fail'
total_gcode = ''
for tooluid_key in list(tools_dict.keys()):
tool_cnt += 1
dia_cnc_dict = deepcopy(tools_dict[tooluid_key])
@ -2123,6 +2129,7 @@ class GeometryObject(FlatCAMObj, Geometry):
return 'fail'
else:
dia_cnc_dict['gcode'] = res
total_gcode += res
self.app.inform.emit('[success] %s' % _("G-Code parsing in progress..."))
dia_cnc_dict['gcode_parsed'] = job_obj.gcode_parse()
@ -2149,6 +2156,8 @@ class GeometryObject(FlatCAMObj, Geometry):
})
dia_cnc_dict.clear()
job_obj.source_file = total_gcode
if use_thread:
# To be run in separate thread
def job_thread(a_obj):
@ -2288,17 +2297,18 @@ class GeometryObject(FlatCAMObj, Geometry):
# it seems that the tolerance needs to be a lot lower value than 0.01 and it was hardcoded initially
# to a value of 0.0005 which is 20 times less than 0.01
tol = float(self.app.defaults['global_tolerance']) / 20
job_obj.generate_from_geometry_2(
self, tooldia=tooldia, offset=offset, tolerance=tol,
z_cut=z_cut, z_move=z_move,
feedrate=feedrate, feedrate_z=feedrate_z, feedrate_rapid=feedrate_rapid,
spindlespeed=spindlespeed, dwell=dwell, dwelltime=dwelltime,
multidepth=multidepth, depthpercut=depthperpass,
toolchange=toolchange, toolchangez=toolchangez, toolchangexy=toolchangexy,
extracut=extracut, extracut_length=extracut_length, startz=startz, endz=endz, endxy=endxy,
pp_geometry_name=ppname_g
res = job_obj.generate_from_geometry_2(self, tooldia=tooldia, offset=offset, tolerance=tol,
z_cut=z_cut, z_move=z_move, feedrate=feedrate,
feedrate_z=feedrate_z, feedrate_rapid=feedrate_rapid,
spindlespeed=spindlespeed, dwell=dwell, dwelltime=dwelltime,
multidepth=multidepth, depthpercut=depthperpass,
toolchange=toolchange, toolchangez=toolchangez,
toolchangexy=toolchangexy,
extracut=extracut, extracut_length=extracut_length,
startz=startz, endz=endz, endxy=endxy,
pp_geometry_name=ppname_g
)
job_obj.source_file = res
# tell gcode_parse from which point to start drawing the lines depending on what kind of object is the
# source of gcode
job_obj.toolchange_xy_type = "geometry"

View File

@ -1797,6 +1797,7 @@ class ToolDrilling(AppTool, Excellon):
self.total_gcode_parsed += tool_gcode_parsed
job_obj.gcode = self.total_gcode
job_obj.source_file = self.total_gcode
job_obj.gcode_parsed = self.total_gcode_parsed
if job_obj.gcode == 'fail':
return 'fail'

View File

@ -913,6 +913,7 @@ class SolderPaste(AppTool):
job_obj.options['xmax'] = xmax
job_obj.options['ymax'] = ymax
total_gcode = ''
for tooluid_key, tooluid_value in obj.tools.items():
# find the tool_dia associated with the tooluid_key
tool_dia = tooluid_value['tooldia']
@ -934,6 +935,7 @@ class SolderPaste(AppTool):
return 'fail'
else:
tool_cnc_dict['gcode'] = res
total_gcode += res
# ## PARSE GCODE # ##
tool_cnc_dict['gcode_parsed'] = job_obj.gcode_parse()
@ -949,6 +951,8 @@ class SolderPaste(AppTool):
})
tool_cnc_dict.clear()
job_obj.source_file = total_gcode
if use_thread:
# To be run in separate thread
def job_thread(app_obj):

View File

@ -83,6 +83,7 @@ from appEditors.AppGeoEditor import AppGeoEditor
from appEditors.AppExcEditor import AppExcEditor
from appEditors.AppGerberEditor import AppGerberEditor
from appEditors.AppTextEditor import AppTextEditor
from appEditors.appGCodeEditor import AppGCodeEditor
from appParsers.ParseHPGL2 import HPGL2
# FlatCAM Workers
@ -1574,6 +1575,12 @@ class App(QtCore.QObject):
self.grb_editor = AppGerberEditor(self)
except Exception as es:
log.debug("app_Main.__init__() --> Gerber Editor Error: %s" % str(es))
try:
self.gcode_editor = AppGCodeEditor(self)
except Exception as es:
log.debug("app_Main.__init__() --> GCode Editor Error: %s" % str(es))
self.log.debug("Finished adding FlatCAM Editor's.")
self.set_ui_title(name=_("New Project - Not saved"))
@ -2186,9 +2193,10 @@ class App(QtCore.QObject):
if edited_object.tools[tool]['tooldia'] == selected_tooldia:
multi_tool = tool
break
log.debug("Editing MultiGeo Geometry with tool diameter: %s" % str(multi_tool))
self.geo_editor.edit_fcgeometry(edited_object, multigeo_tool=multi_tool)
else:
log.debug("Editing SingleGeo Geometry with tool diameter.")
self.geo_editor.edit_fcgeometry(edited_object)
# set call source to the Editor we go into
@ -2226,7 +2234,10 @@ class App(QtCore.QObject):
if self.ui.splitter.sizes()[0] == 0:
self.ui.splitter.setSizes([1, 1])
edited_object.on_edit_code_click()
# set call source to the Editor we go into
self.call_source = 'gcode_editor'
self.gcode_editor.edit_fcgcode(edited_object)
return
# make sure that we can't select another object while in Editor Mode:
@ -7762,10 +7773,7 @@ class App(QtCore.QObject):
# then append the text from GCode to the text editor
if obj.kind == 'cncjob':
try:
file = obj.export_gcode(
preamble=self.defaults["cncjob_prepend"],
postamble=self.defaults["cncjob_append"],
to_file=True)
file = obj.export_gcode(to_file=True)
if file == 'fail':
return 'fail'
except AttributeError:
@ -8682,14 +8690,14 @@ class App(QtCore.QObject):
def job_thread_grb(app_obj):
ret = make_gerber()
if ret == 'fail':
self.inform.emit('[ERROR_NOTCL] %s' % _('Could not export Gerber file.'))
self.inform.emit('[ERROR_NOTCL] %s' % _('Could not export file.'))
return
self.worker_task.emit({'fcn': job_thread_grb, 'params': [self]})
else:
gret = make_gerber()
if gret == 'fail':
self.inform.emit('[ERROR_NOTCL] %s' % _('Could not export Gerber file.'))
self.inform.emit('[ERROR_NOTCL] %s' % _('Could not export file.'))
return 'fail'
if local_use is not None:
return gret
@ -8790,9 +8798,15 @@ class App(QtCore.QObject):
units = self.defaults['units'].upper()
def obj_init(geo_obj, app_obj):
geo_obj.import_svg(filename, obj_type, units=units)
geo_obj.multigeo = False
geo_obj.source_file = self.export_gerber(obj_name=name, filename=None, local_use=geo_obj, use_thread=False)
if obj_type == "geometry":
geo_obj.import_svg(filename, obj_type, units=units)
elif obj_type == "gerber":
geo_obj.import_svg(filename, obj_type, units=units)
geo_obj.multigeo = True
with open(filename) as f:
file_content = f.read()
geo_obj.source_file = file_content
with self.proc_container.new(_("Importing SVG")) as proc:
@ -8843,7 +8857,11 @@ class App(QtCore.QObject):
geo_obj.import_dxf_as_gerber(filename, units=units)
else:
return "fail"
geo_obj.multigeo = True
with open(filename) as f:
file_content = f.read()
geo_obj.source_file = file_content
with self.proc_container.new(_("Importing DXF")):

View File

@ -1058,7 +1058,19 @@ class Geometry(object):
geos = [translate(scale(g, 1.0, -1.0, origin=(0, 0)), yoff=h) for g in geos]
# trying to optimize the resulting geometry by merging contiguous lines
geos = linemerge(geos)
geos = list(self.flatten_list(geos))
geos_polys = []
geos_lines = []
for g in geos:
if isinstance(g, Polygon):
geos_polys.append(g)
else:
geos_lines.append(g)
merged_lines = linemerge(geos_lines)
geos = geos_polys
for l in merged_lines:
geos.append(l)
# Add to object
if self.solid_geometry is None:
@ -1081,12 +1093,31 @@ class Geometry(object):
if flip:
# Change origin to bottom left
for i in geos_text:
_, minimy, _, maximy = i.bounds
__, minimy, __, maximy = i.bounds
h2 = (maximy - minimy) * 0.5
geos_text_f.append(translate(scale(i, 1.0, -1.0, origin=(0, 0)), yoff=(h + h2)))
if geos_text_f:
self.solid_geometry = self.solid_geometry + geos_text_f
tooldia = float(self.app.defaults["geometry_cnctooldia"])
tooldia = float('%.*f' % (self.decimals, tooldia))
new_data = {k: v for k, v in self.options.items()}
self.tools.update({
1: {
'tooldia': tooldia,
'offset': 'Path',
'offset_value': 0.0,
'type': _('Rough'),
'tool_type': 'C1',
'data': deepcopy(new_data),
'solid_geometry': self.solid_geometry
}
})
self.tools[1]['data']['name'] = self.options['name']
def import_dxf_as_geo(self, filename, units='MM'):
"""
Imports shapes from an DXF file into the object's geometry.
@ -1103,7 +1134,19 @@ class Geometry(object):
geos = getdxfgeo(dxf)
# trying to optimize the resulting geometry by merging contiguous lines
geos = linemerge(geos)
geos = list(self.flatten_list(geos))
geos_polys = []
geos_lines = []
for g in geos:
if isinstance(g, Polygon):
geos_polys.append(g)
else:
geos_lines.append(g)
merged_lines = linemerge(geos_lines)
geos = geos_polys
for l in merged_lines:
geos.append(l)
# Add to object
if self.solid_geometry is None:
@ -5176,7 +5219,8 @@ class CNCjob(Geometry):
geo_storage = {}
for geo in temp_solid_geometry:
geo_storage[geo.coords[0]] = geo
if not geo is None:
geo_storage[geo.coords[0]] = geo
locations = list(geo_storage.keys())
if opt_type == 'M':

111
preprocessors/Z_laser.py Normal file
View File

@ -0,0 +1,111 @@
# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# http://flatcam.org #
# File Author: Matthieu Berthomé #
# Date: 5/26/2017 #
# MIT Licence #
# ##########################################################
from appPreProcessor import *
# This post processor is configured to output code that
# is compatible with almost any version of Grbl.
class Z_laser(PreProc):
include_header = True
coordinate_format = "%.*f"
feedrate_format = '%.*f'
def start_code(self, p):
units = ' ' + str(p['units']).lower()
gcode = '(This preprocessor is used with a motion controller loaded with GRBL firmware. )\n'
gcode += '(It is for the case when it is used together with a LASER connected on the SPINDLE connector.)\n'
gcode += '(On toolchange event the laser will move to a defined Z height to change the laser dot size.)\n\n'
xmin = '%.*f' % (p.coords_decimals, p['options']['xmin'])
xmax = '%.*f' % (p.coords_decimals, p['options']['xmax'])
ymin = '%.*f' % (p.coords_decimals, p['options']['ymin'])
ymax = '%.*f' % (p.coords_decimals, p['options']['ymax'])
gcode += '(Feedrate: ' + str(p['feedrate']) + units + '/min' + ')\n'
gcode += '(Feedrate rapids: ' + str(p['feedrate_rapid']) + units + '/min' + ')\n' + '\n'
gcode += '(Z Focus: ' + str(p['z_move']) + units + ')\n'
gcode += '(Steps per circle: ' + str(p['steps_per_circle']) + ')\n'
if str(p['options']['type']) == 'Excellon' or str(p['options']['type']) == 'Excellon Geometry':
gcode += '(Preprocessor Excellon: ' + str(p['pp_excellon_name']) + ')\n'
else:
gcode += '(Preprocessor Geometry: ' + str(p['pp_geometry_name']) + ')\n' + '\n'
gcode += '(X range: ' + '{: >9s}'.format(xmin) + ' ... ' + '{: >9s}'.format(xmax) + ' ' + units + ')\n'
gcode += '(Y range: ' + '{: >9s}'.format(ymin) + ' ... ' + '{: >9s}'.format(ymax) + ' ' + units + ')\n\n'
gcode += '(Laser Power (Spindle Speed): ' + str(p['spindlespeed']) + ')\n\n'
gcode += ('G20' if p.units.upper() == 'IN' else 'G21') + "\n"
gcode += 'G90\n'
gcode += 'G17\n'
gcode += 'G94'
return gcode
def startz_code(self, p):
return ''
def lift_code(self, p):
return 'M5'
def down_code(self, p):
sdir = {'CW': 'M03', 'CCW': 'M04'}[p.spindledir]
if p.spindlespeed:
return '%s S%s' % (sdir, str(p.spindlespeed))
else:
return sdir
def toolchange_code(self, p):
return 'G00 Z' + self.coordinate_format % (p.coords_decimals, p.z_move)
def up_to_zero_code(self, p):
return 'M5'
def position_code(self, p):
return ('X' + self.coordinate_format + ' Y' + self.coordinate_format) % \
(p.coords_decimals, p.x, p.coords_decimals, p.y)
def rapid_code(self, p):
return ('G00 ' + self.position_code(p)).format(**p)
def linear_code(self, p):
return ('G01 ' + self.position_code(p)).format(**p) + \
' F' + str(self.feedrate_format % (p.fr_decimals, p.feedrate))
def end_code(self, p):
coords_xy = p['xy_end']
gcode = ('G00 Z' + self.feedrate_format % (p.fr_decimals, p.z_end) + "\n")
if coords_xy and coords_xy != '':
gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n"
return gcode
def feedrate_code(self, p):
return 'G01 F' + str(self.feedrate_format % (p.fr_decimals, p.feedrate))
def z_feedrate_code(self, p):
return 'G01 F' + str(self.feedrate_format % (p.fr_decimals, p.z_feedrate))
def spindle_code(self, p):
sdir = {'CW': 'M03', 'CCW': 'M04'}[p.spindledir]
if p.spindlespeed:
return '%s S%s' % (sdir, str(p.spindlespeed))
else:
return sdir
def dwell_code(self, p):
return ''
def spindle_stop_code(self, p):
return 'M5'

View File

@ -332,6 +332,7 @@ class TclCommandDrillcncjob(TclCommandSignaled):
job_obj.excellon_optimization_type = opt_type
ret_val = job_obj.generate_from_excellon_by_tool(obj, tools, use_ui=False)
job_obj.source_file = ret_val
if ret_val == 'fail':
return 'fail'