Merge branch 'Beta'
21
CHANGELOG.md
|
@ -7,6 +7,27 @@ CHANGELOG for FlatCAM beta
|
|||
|
||||
=================================================
|
||||
|
||||
2.05.2020
|
||||
|
||||
- changed the icons for the grid snap in the status bar
|
||||
- moved some of the methods from FlatCAMApp.App to flatcamGUI.FlatCAMGUI class
|
||||
- fixed bug in Gerber Editor in which the units conversion wasn't calculated correct
|
||||
- fixed bug in Gerber Editor in which the QThread that is started on object edit was not stopped at clean up stage
|
||||
- fixed bug in Gerber Editor that kept all the apertures (including the geometry) of a previously edited object that was not saved after edit
|
||||
- modified the Cutout Tool to generate multi-geo objects therefore the set geometry parameters will populate the Geometry Object UI
|
||||
- modified the Panelize Tool to optimize the output from Cutout Tool such that there are no longer overlapping cuts
|
||||
- some string corrections
|
||||
- updated the Italian translation done by user @pcb-hobbyst
|
||||
- RELEASE 8.992
|
||||
|
||||
01.05.2020
|
||||
|
||||
- added some ToolTips (strings needed to be translated too) for the Cut Z entry in Geometry Object UI that explain why is sometime disabled and reason for it's value (sometime is zero)
|
||||
- solve parenting issues when trying to load a FlatScript from Menu -> File -> Scripting
|
||||
- added a first new example script and added some files to work with
|
||||
- added a new parameter that will store the home folder of the FlatCAM installation so we can access the example folder
|
||||
- added in Gerber editor a method for zoom fit that takes into consideration the current geometry of the edited object
|
||||
|
||||
30.04.2020
|
||||
|
||||
- made some corrections - due of recent refactoring PyCharm reported errors all over (not correct but it made programming difficult)
|
||||
|
|
233
FlatCAMApp.py
|
@ -163,7 +163,7 @@ class App(QtCore.QObject):
|
|||
# ################################### Version and VERSION DATE ##################################################
|
||||
# ###############################################################################################################
|
||||
version = 8.992
|
||||
version_date = "2020/05/01"
|
||||
version_date = "2020/05/03"
|
||||
beta = True
|
||||
engine = '3D'
|
||||
|
||||
|
@ -432,6 +432,9 @@ class App(QtCore.QObject):
|
|||
# ################################# DEFAULTS - PREFERENCES STORAGE ###########################################
|
||||
# ############################################################################################################
|
||||
self.defaults = FlatCAMDefaults()
|
||||
|
||||
self.defaults["root_folder_path"] = self.app_home
|
||||
|
||||
current_defaults_path = os.path.join(self.data_path, "current_defaults.FlatConfig")
|
||||
if user_defaults:
|
||||
self.defaults.load(filename=current_defaults_path)
|
||||
|
@ -503,7 +506,6 @@ class App(QtCore.QObject):
|
|||
QtCore.QObject.__init__(self)
|
||||
|
||||
self.ui = FlatCAMGUI(self)
|
||||
self.on_grid_snap_triggered(state=True)
|
||||
|
||||
theme_settings = QtCore.QSettings("Open Source", "FlatCAM")
|
||||
if theme_settings.contains("theme"):
|
||||
|
@ -1071,9 +1073,6 @@ class App(QtCore.QObject):
|
|||
# signal emitted when a tab is closed in the Plot Area
|
||||
self.ui.plot_tab_area.tab_closed_signal.connect(self.on_plot_area_tab_closed)
|
||||
|
||||
self.ui.grid_snap_btn.triggered.connect(self.on_grid_snap_triggered)
|
||||
self.ui.snap_infobar_label.clicked.connect(self.on_grid_icon_snap_clicked)
|
||||
|
||||
# signal to close the application
|
||||
self.close_app_signal.connect(self.kill_app)
|
||||
# ################################# FINISHED CONNECTING SIGNALS #############################################
|
||||
|
@ -2908,45 +2907,37 @@ class App(QtCore.QObject):
|
|||
|
||||
self.new_object('gerber', 'new_grb', initialize, plot=False)
|
||||
|
||||
def new_script_object(self, name=None, text=None):
|
||||
def new_script_object(self):
|
||||
"""
|
||||
Creates a new, blank TCL Script object.
|
||||
:param name: a name for the new object
|
||||
:param text: pass a source file to the newly created script to be loaded in it
|
||||
|
||||
:return: None
|
||||
"""
|
||||
self.defaults.report_usage("new_script_object()")
|
||||
|
||||
if text is not None:
|
||||
new_source_file = text
|
||||
else:
|
||||
# commands_list = "# AddCircle, AddPolygon, AddPolyline, AddRectangle, AlignDrill, " \
|
||||
# "AlignDrillGrid, Bbox, Bounds, ClearShell, CopperClear,\n" \
|
||||
# "# Cncjob, Cutout, Delete, Drillcncjob, ExportDXF, ExportExcellon, ExportGcode,\n" \
|
||||
# "# ExportGerber, ExportSVG, Exteriors, Follow, GeoCutout, GeoUnion, GetNames,\n" \
|
||||
# "# GetSys, ImportSvg, Interiors, Isolate, JoinExcellon, JoinGeometry, " \
|
||||
# "ListSys, MillDrills,\n" \
|
||||
# "# MillSlots, Mirror, New, NewExcellon, NewGeometry, NewGerber, Nregions, " \
|
||||
# "Offset, OpenExcellon, OpenGCode, OpenGerber, OpenProject,\n" \
|
||||
# "# Options, Paint, Panelize, PlotAl, PlotObjects, SaveProject, " \
|
||||
# "SaveSys, Scale, SetActive, SetSys, SetOrigin, Skew, SubtractPoly,\n" \
|
||||
# "# SubtractRectangle, Version, WriteGCode\n"
|
||||
# commands_list = "# AddCircle, AddPolygon, AddPolyline, AddRectangle, AlignDrill, " \
|
||||
# "AlignDrillGrid, Bbox, Bounds, ClearShell, CopperClear,\n" \
|
||||
# "# Cncjob, Cutout, Delete, Drillcncjob, ExportDXF, ExportExcellon, ExportGcode,\n" \
|
||||
# "# ExportGerber, ExportSVG, Exteriors, Follow, GeoCutout, GeoUnion, GetNames,\n" \
|
||||
# "# GetSys, ImportSvg, Interiors, Isolate, JoinExcellon, JoinGeometry, " \
|
||||
# "ListSys, MillDrills,\n" \
|
||||
# "# MillSlots, Mirror, New, NewExcellon, NewGeometry, NewGerber, Nregions, " \
|
||||
# "Offset, OpenExcellon, OpenGCode, OpenGerber, OpenProject,\n" \
|
||||
# "# Options, Paint, Panelize, PlotAl, PlotObjects, SaveProject, " \
|
||||
# "SaveSys, Scale, SetActive, SetSys, SetOrigin, Skew, SubtractPoly,\n" \
|
||||
# "# SubtractRectangle, Version, WriteGCode\n"
|
||||
|
||||
new_source_file = '# %s\n' % _('CREATE A NEW FLATCAM TCL SCRIPT') + \
|
||||
'# %s:\n' % _('TCL Tutorial is here') + \
|
||||
'# https://www.tcl.tk/man/tcl8.5/tutorial/tcltutorial.html\n' + '\n\n' + \
|
||||
'# %s:\n' % _("FlatCAM commands list")
|
||||
new_source_file += '# %s\n\n' % _("Type >help< followed by Run Code for a list of FlatCAM Tcl Commands "
|
||||
"(displayed in Tcl Shell).")
|
||||
new_source_file = '# %s\n' % _('CREATE A NEW FLATCAM TCL SCRIPT') + \
|
||||
'# %s:\n' % _('TCL Tutorial is here') + \
|
||||
'# https://www.tcl.tk/man/tcl8.5/tutorial/tcltutorial.html\n' + '\n\n' + \
|
||||
'# %s:\n' % _("FlatCAM commands list")
|
||||
new_source_file += '# %s\n\n' % _("Type >help< followed by Run Code for a list of FlatCAM Tcl Commands "
|
||||
"(displayed in Tcl Shell).")
|
||||
|
||||
def initialize(obj, app):
|
||||
obj.source_file = deepcopy(new_source_file)
|
||||
|
||||
if name is None:
|
||||
outname = 'new_script'
|
||||
else:
|
||||
outname = name
|
||||
|
||||
outname = 'new_script'
|
||||
self.new_object('script', outname, initialize, plot=False)
|
||||
|
||||
def new_document_object(self):
|
||||
|
@ -3268,7 +3259,7 @@ class App(QtCore.QObject):
|
|||
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "<marius_adrian@yahoo.com>"), 4, 2)
|
||||
self.prog_grid_lay.addWidget(QtWidgets.QLabel(''), 5, 0)
|
||||
|
||||
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Alex Lazar"), 6, 0)
|
||||
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "David Robertson"), 6, 0)
|
||||
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Matthieu Berthomé"), 7, 0)
|
||||
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Mike Evans"), 8, 0)
|
||||
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Victor Benso"), 9, 0)
|
||||
|
@ -3300,6 +3291,7 @@ class App(QtCore.QObject):
|
|||
|
||||
self.prog_grid_lay.addWidget(QtWidgets.QLabel(''), 63, 0)
|
||||
|
||||
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Alex Lazar"), 64, 0)
|
||||
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Chris Breneman"), 65, 0)
|
||||
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Eric Varsanyi"), 67, 0)
|
||||
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Lubos Medovarsky"), 69, 0)
|
||||
|
@ -3336,27 +3328,43 @@ class App(QtCore.QObject):
|
|||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('<b>%s</b>' % _("Translator")), 0, 1)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('<b>%s</b>' % _("Corrections")), 0, 2)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('<b>%s</b>' % _("E-mail")), 0, 3)
|
||||
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "BR - Portuguese"), 1, 0)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Carlos Stein"), 1, 1)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "<carlos.stein@gmail.com>"), 1, 3)
|
||||
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "French"), 2, 0)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu (Google-Tr)"), 2, 1)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % ""), 2, 2)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % " "), 2, 3)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "German"), 3, 0)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu (Google-Tr)"), 3, 1)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Jens Karstedt, Detlef Eckardt"), 3, 2)
|
||||
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Hungarian"), 3, 0)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % " "), 3, 1)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % " "), 3, 2)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % " "), 3, 3)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Romanian"), 4, 0)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu"), 4, 1)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "<marius_adrian@yahoo.com>"), 4, 3)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Russian"), 5, 0)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Andrey Kultyapov"), 5, 1)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "<camellan@yandex.ru>"), 5, 3)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Spanish"), 6, 0)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu (Google-Tr)"), 6, 1)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % ""), 6, 2)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % " "), 6, 3)
|
||||
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Italian"), 4, 0)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Golfetto Massimiliano"), 4, 1)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % " "), 4, 2)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "pcb@golfetto.eu"), 4, 3)
|
||||
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "German"), 5, 0)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu (Google-Tr)"), 5, 1)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Jens Karstedt, Detlef Eckardt"), 5, 2)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % " "), 5, 3)
|
||||
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Romanian"), 6, 0)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu"), 6, 1)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "<marius_adrian@yahoo.com>"), 6, 3)
|
||||
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Russian"), 7, 0)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Andrey Kultyapov"), 7, 1)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "<camellan@yandex.ru>"), 7, 3)
|
||||
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Spanish"), 8, 0)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu (Google-Tr)"), 8, 1)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % ""), 8, 2)
|
||||
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % " "), 8, 3)
|
||||
self.translator_grid_lay.setColumnStretch(0, 0)
|
||||
self.translators_tab_layout.addStretch()
|
||||
|
||||
|
@ -4120,9 +4128,11 @@ class App(QtCore.QObject):
|
|||
obj.multigeo = True
|
||||
for tooluid, dict_value in obj.tools.items():
|
||||
dict_value['solid_geometry'] = deepcopy(obj.solid_geometry)
|
||||
|
||||
if not isinstance(obj.solid_geometry, list):
|
||||
obj.solid_geometry = [obj.solid_geometry]
|
||||
obj.solid_geometry[:] = []
|
||||
|
||||
# obj.solid_geometry[:] = []
|
||||
obj.plot()
|
||||
|
||||
self.should_we_save = True
|
||||
|
@ -4639,7 +4649,7 @@ class App(QtCore.QObject):
|
|||
self.defaults.report_usage("on_toggle_grid()")
|
||||
|
||||
self.ui.grid_snap_btn.trigger()
|
||||
self.on_grid_snap_triggered(state=True)
|
||||
self.ui.on_grid_snap_triggered(state=True)
|
||||
|
||||
def on_toggle_grid_lines(self):
|
||||
self.defaults.report_usage("on_toggle_grd_lines()")
|
||||
|
@ -5033,7 +5043,7 @@ class App(QtCore.QObject):
|
|||
self.paste_tool.on_add_tool_by_key()
|
||||
|
||||
# It's meant to delete tools in tool tables via a 'Delete' shortcut key but only if certain conditions are met
|
||||
# See description bellow.
|
||||
# See description below.
|
||||
def on_delete_keypress(self):
|
||||
notebook_widget_name = self.ui.notebook.currentWidget().objectName()
|
||||
|
||||
|
@ -6949,7 +6959,6 @@ class App(QtCore.QObject):
|
|||
else:
|
||||
|
||||
key_modifier = QtWidgets.QApplication.keyboardModifiers()
|
||||
|
||||
if key_modifier == QtCore.Qt.ShiftModifier:
|
||||
mod_key = 'Shift'
|
||||
elif key_modifier == QtCore.Qt.ControlModifier:
|
||||
|
@ -6958,20 +6967,19 @@ class App(QtCore.QObject):
|
|||
mod_key = None
|
||||
|
||||
try:
|
||||
if mod_key == self.defaults["global_mselect_key"]:
|
||||
if self.command_active is None:
|
||||
# If the CTRL key is pressed when the LMB is clicked then if the object is selected it will
|
||||
# deselect, and if it's not selected then it will be selected
|
||||
# If there is no active command (self.command_active is None) then we check if we clicked
|
||||
# on a object by checking the bounding limits against mouse click position
|
||||
if self.command_active is None:
|
||||
if mod_key == self.defaults["global_mselect_key"]:
|
||||
self.select_objects(key='multisel')
|
||||
self.delete_hover_shape()
|
||||
else:
|
||||
# If there is no active command (self.command_active is None) then we check if we clicked
|
||||
# on a object by checking the bounding limits against mouse click position
|
||||
if self.command_active is None:
|
||||
else:
|
||||
# If there is no active command (self.command_active is None) then we check if
|
||||
# we clicked on a object by checking the bounding limits against mouse click position
|
||||
self.select_objects()
|
||||
self.delete_hover_shape()
|
||||
|
||||
self.delete_hover_shape()
|
||||
except Exception as e:
|
||||
log.warning("FlatCAMApp.on_mouse_click_release_over_plot() select click --> Error: %s" % str(e))
|
||||
return
|
||||
|
@ -8167,7 +8175,6 @@ class App(QtCore.QObject):
|
|||
# ###############################################################################################################
|
||||
# ### The following section has the functions that are displayed and call the Editor tab CNCJob Tab #############
|
||||
# ###############################################################################################################
|
||||
|
||||
def init_code_editor(self, name):
|
||||
|
||||
self.text_editor_tab = TextEditor(app=self, plain_text=True)
|
||||
|
@ -8339,14 +8346,14 @@ class App(QtCore.QObject):
|
|||
# set cursor of the code editor with the cursor at the searcehd line
|
||||
self.ui.plot_tab_area.currentWidget().code_editor.setTextCursor(cursor)
|
||||
|
||||
def on_filenewscript(self, silent=False, name=None, text=None):
|
||||
def on_filenewscript(self, silent=False):
|
||||
"""
|
||||
Will create a new script file and open it in the Code Editor
|
||||
|
||||
:param silent: if True will not display status messages
|
||||
:param name: if specified will be the name of the new script
|
||||
:param text: pass a source file to the newly created script to be loaded in it
|
||||
:return: None
|
||||
:param silent: if True will not display status messages
|
||||
:param name: if specified will be the name of the new script
|
||||
:param text: pass a source file to the newly created script to be loaded in it
|
||||
:return: None
|
||||
"""
|
||||
if silent is False:
|
||||
self.inform.emit('[success] %s' % _("New TCL script file created in Code Editor."))
|
||||
|
@ -8355,10 +8362,7 @@ class App(QtCore.QObject):
|
|||
self.ui.position_label.setText("")
|
||||
self.ui.rel_position_label.setText("")
|
||||
|
||||
if name is not None:
|
||||
self.new_script_object(name=name, text=text)
|
||||
else:
|
||||
self.new_script_object(text=text)
|
||||
self.new_script_object()
|
||||
|
||||
# script_text = script_obj.source_file
|
||||
#
|
||||
|
@ -8372,9 +8376,9 @@ class App(QtCore.QObject):
|
|||
"""
|
||||
Will open a Tcl script file into the Code Editor
|
||||
|
||||
:param silent: if True will not display status messages
|
||||
:param name: name of a Tcl script file to open
|
||||
:return:
|
||||
:param silent: if True will not display status messages
|
||||
:param name: name of a Tcl script file to open
|
||||
:return: None
|
||||
"""
|
||||
|
||||
self.defaults.report_usage("on_fileopenscript")
|
||||
|
@ -9609,27 +9613,46 @@ class App(QtCore.QObject):
|
|||
:param silent: If True there will be no messages printed to StatusBar
|
||||
:return: None
|
||||
"""
|
||||
|
||||
def obj_init(script_obj, app_obj):
|
||||
|
||||
assert isinstance(script_obj, ScriptObject), \
|
||||
"Expected to initialize a ScriptObject but got %s" % type(script_obj)
|
||||
|
||||
if silent is False:
|
||||
app_obj.inform.emit('[success] %s' % _("TCL script file opened in Code Editor."))
|
||||
|
||||
try:
|
||||
script_obj.parse_file(filename)
|
||||
except IOError:
|
||||
app_obj.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Failed to open file"), filename))
|
||||
return "fail"
|
||||
except ParseError as err:
|
||||
app_obj.inform.emit('[ERROR_NOTCL] %s: %s. %s' % (_("Failed to parse file"), filename, str(err)))
|
||||
app_obj.log.error(str(err))
|
||||
return "fail"
|
||||
except Exception as e:
|
||||
log.debug("App.open_script() -> %s" % str(e))
|
||||
msg = '[ERROR] %s' % _("An internal error has occurred. See shell.\n")
|
||||
msg += traceback.format_exc()
|
||||
app_obj.inform.emit(msg)
|
||||
return "fail"
|
||||
|
||||
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)
|
||||
# Object creation
|
||||
ret_val = self.new_object("script", script_name, obj_init, autoselected=False, plot=False)
|
||||
if ret_val == 'fail':
|
||||
filename = self.defaults['global_tcl_path'] + '/' + script_name
|
||||
ret_val = self.new_object("script", script_name, obj_init, autoselected=False, plot=False)
|
||||
if ret_val == 'fail':
|
||||
self.inform.emit('[ERROR_NOTCL]%s' % _('Failed to open TCL Script.'))
|
||||
return 'fail'
|
||||
|
||||
# Register recent file
|
||||
self.file_opened.emit("script", filename)
|
||||
|
@ -10611,29 +10634,6 @@ class App(QtCore.QObject):
|
|||
update_colors=(new_color, new_line_color)
|
||||
)
|
||||
|
||||
def on_grid_snap_triggered(self, state):
|
||||
"""
|
||||
|
||||
:param state: A parameter with the state of the grid, boolean
|
||||
|
||||
:return:
|
||||
"""
|
||||
if state:
|
||||
self.ui.snap_infobar_label.setPixmap(QtGui.QPixmap(self.resource_location + '/snap_filled_16.png'))
|
||||
else:
|
||||
self.ui.snap_infobar_label.setPixmap(QtGui.QPixmap(self.resource_location + '/snap_16.png'))
|
||||
|
||||
self.ui.snap_infobar_label.clicked_state = state
|
||||
|
||||
def on_grid_icon_snap_clicked(self):
|
||||
"""
|
||||
Slot called by clicking a GUI element, in this case a FCLabel
|
||||
|
||||
:return:
|
||||
"""
|
||||
if isinstance(self.sender(), FCLabel):
|
||||
self.ui.grid_snap_btn.trigger()
|
||||
|
||||
def generate_cnc_job(self, objects):
|
||||
"""
|
||||
Slot that will be called by clicking an entry in the contextual menu generated in the Project Tab tree
|
||||
|
@ -10836,21 +10836,6 @@ class App(QtCore.QObject):
|
|||
# no_km)
|
||||
# QtWidgets.qApp.sendEvent(self.shell._edit, f)
|
||||
|
||||
def on_toggle_shell_from_settings(self, state):
|
||||
"""
|
||||
Toggle shell: if is visible close it, if it is closed then open it
|
||||
:return: None
|
||||
"""
|
||||
|
||||
self.defaults.report_usage("on_toggle_shell_from_settings()")
|
||||
|
||||
if state is True:
|
||||
if not self.ui.shell_dock.isVisible():
|
||||
self.ui.shell_dock.show()
|
||||
else:
|
||||
if self.ui.shell_dock.isVisible():
|
||||
self.ui.shell_dock.hide()
|
||||
|
||||
def shell_message(self, msg, show=False, error=False, warning=False, success=False, selected=False):
|
||||
"""
|
||||
Shows a message on the FlatCAM Shell
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
G04*
|
||||
G04 GERBER (RE)GENERATED BY FLATCAM v8.992 - www.flatcam.org - Version Date: 2020/05/01*
|
||||
G04 Filename: test.gbr*
|
||||
G04 Created on : Friday, 01 May 2020 at 17:03*
|
||||
%INBottom.gbr*%
|
||||
%MOMM*%
|
||||
%ADD13C,0.8*%
|
||||
%ADD14C,1.27*%
|
||||
%ADD15C,1.27*%
|
||||
%ADD16R,1.5X1.5*%
|
||||
%ADD17C,1.5*%
|
||||
%FSLAX53Y53*%
|
||||
G04*
|
||||
G71*
|
||||
G90*
|
||||
G75*
|
||||
G01*
|
||||
%LNBottom*%
|
||||
%LPD*%
|
||||
X20779Y24462D2*
|
||||
D13*
|
||||
Y27002D1*
|
||||
X18229D1*
|
||||
X14429D1*
|
||||
X18229D2*
|
||||
X19509D1*
|
||||
Y28272D1*
|
||||
X32209Y14302D2*
|
||||
X28399D1*
|
||||
Y16842D1*
|
||||
Y14302D2*
|
||||
X18239D1*
|
||||
X20779Y16842D2*
|
||||
X23329D1*
|
||||
Y28272D1*
|
||||
X27129D1*
|
||||
X20779Y19382D2*
|
||||
X14429D1*
|
||||
X20779Y21922D2*
|
||||
X18239D1*
|
||||
X28399D2*
|
||||
X32209D1*
|
||||
D14*
|
||||
X27129Y28272D3*
|
||||
D15*
|
||||
X19509D3*
|
||||
D14*
|
||||
X14429Y19382D3*
|
||||
D15*
|
||||
Y27002D3*
|
||||
D14*
|
||||
X18239Y21922D3*
|
||||
D15*
|
||||
Y14302D3*
|
||||
D14*
|
||||
X32209Y21922D3*
|
||||
D15*
|
||||
Y14302D3*
|
||||
D16*
|
||||
X28399Y24462D3*
|
||||
D17*
|
||||
Y21922D3*
|
||||
Y19382D3*
|
||||
Y16842D3*
|
||||
X20779D3*
|
||||
Y19382D3*
|
||||
Y21922D3*
|
||||
Y24462D3*
|
||||
M02*
|
|
@ -0,0 +1,28 @@
|
|||
M48
|
||||
;EXCELLON GENERATED BY FLATCAM v8.992 - www.flatcam.org - Version Date: 2020/05/01
|
||||
;Filename: test.txt
|
||||
;Created on : Friday, 01 May 2020 at 17:04
|
||||
INCH,LZ
|
||||
;FILE_FORMAT=2:4
|
||||
T1F00S00C0.0220
|
||||
T2F00S00C0.0354
|
||||
%
|
||||
T01
|
||||
X010681Y011130
|
||||
X007681Y011130
|
||||
X005681Y010630
|
||||
X007181Y008630
|
||||
X005681Y007630
|
||||
X007181Y005630
|
||||
X012681Y005630
|
||||
X012681Y008630
|
||||
T02
|
||||
X011181Y009630
|
||||
X011181Y008630
|
||||
X011181Y007630
|
||||
X011181Y006630
|
||||
X008181Y006630
|
||||
X008181Y007630
|
||||
X008181Y008630
|
||||
X008181Y009630
|
||||
M30
|
|
@ -0,0 +1,30 @@
|
|||
G04*
|
||||
G04 GERBER (RE)GENERATED BY FLATCAM v8.992 - www.flatcam.org - Version Date: 2020/05/01*
|
||||
G04 Filename: test_1*
|
||||
G04 Created on : Friday, 01 May 2020 at 17:07*
|
||||
G04*
|
||||
G04 RS-274X GERBER GENERATED BY FLATCAM v8.992 - www.flatcam.org - Version Date: 2020/05/01*
|
||||
G04 Filename: test_1_edit*
|
||||
G04 Created on : Friday, 01 May 2020 at 17:06*
|
||||
%FSLAX24Y24*%
|
||||
%MOIN*%
|
||||
%ADD10C,0.003937*%
|
||||
|
||||
G70*
|
||||
G90*
|
||||
G01*
|
||||
%LPD*%
|
||||
D10*
|
||||
X05118Y11417D02*
|
||||
X05118Y05118D01*
|
||||
X05118Y05118D02*
|
||||
X08661Y05118D01*
|
||||
X08661Y05118D02*
|
||||
X08661Y11417D01*
|
||||
X08661Y11417D02*
|
||||
X05118Y11417D01*
|
||||
X08661Y11417D02*
|
||||
X05118Y11417D01*
|
||||
X08661Y11417D02*
|
||||
X05118Y11417D01*
|
||||
M02*
|
|
@ -0,0 +1,19 @@
|
|||
# ----------- START: This is needed only for the examples ----------------
|
||||
# first set the default location where to search for the files to be open and store it to the ROOT_FOLDER variable
|
||||
set ROOT_FOLDER [get_sys root_folder_path]
|
||||
|
||||
# calculate the resources path for the examples we need to run and store it inside the PATH varaible
|
||||
set PATH ${ROOT_FOLDER}/assets/examples/files
|
||||
# ----------- END: This is needed only for the examples ----------------
|
||||
|
||||
# set the working path to the path that holds the files we are going to work with
|
||||
set_path $PATH
|
||||
|
||||
# load the GERBER file
|
||||
open_gerber test.gbr
|
||||
|
||||
# load the Excellon ifle
|
||||
open_excellon test.txt
|
||||
|
||||
# plot them all so we can see them on canvas
|
||||
plot_all
|
Before Width: | Height: | Size: 994 B After Width: | Height: | Size: 553 B |
Before Width: | Height: | Size: 557 B After Width: | Height: | Size: 549 B |
Before Width: | Height: | Size: 337 B After Width: | Height: | Size: 381 B |
Before Width: | Height: | Size: 426 B After Width: | Height: | Size: 586 B |
Before Width: | Height: | Size: 176 B After Width: | Height: | Size: 381 B |
23
camlib.py
|
@ -3472,8 +3472,7 @@ class CNCjob(Geometry):
|
|||
else:
|
||||
log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
|
||||
"The loaded Excellon file has no drills ...")
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s...' %
|
||||
_('The loaded Excellon file has no drills'))
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s...' % _('The loaded Excellon file has no drills'))
|
||||
return 'fail'
|
||||
self.z_cut = deepcopy(old_zcut)
|
||||
log.debug("The total travel distance with Travelling Salesman Algorithm is: %s" % str(measured_distance))
|
||||
|
@ -3499,14 +3498,12 @@ class CNCjob(Geometry):
|
|||
self.app.inform.emit(_("Finished G-Code generation..."))
|
||||
return 'OK'
|
||||
|
||||
def generate_from_multitool_geometry(
|
||||
self, geometry, append=True,
|
||||
tooldia=None, offset=0.0, tolerance=0, z_cut=1.0, z_move=2.0,
|
||||
feedrate=2.0, feedrate_z=2.0, feedrate_rapid=30,
|
||||
spindlespeed=None, spindledir='CW', dwell=False, dwelltime=1.0,
|
||||
multidepth=False, depthpercut=None,
|
||||
toolchange=False, toolchangez=1.0, toolchangexy="0.0, 0.0", extracut=False, extracut_length=0.2,
|
||||
startz=None, endz=2.0, endxy='', pp_geometry_name=None, tool_no=1):
|
||||
def generate_from_multitool_geometry(self, geometry, append=True, tooldia=None, offset=0.0, tolerance=0, z_cut=1.0,
|
||||
z_move=2.0, feedrate=2.0, feedrate_z=2.0, feedrate_rapid=30,
|
||||
spindlespeed=None, spindledir='CW', dwell=False, dwelltime=1.0,
|
||||
multidepth=False, depthpercut=None, toolchange=False, toolchangez=1.0,
|
||||
toolchangexy="0.0, 0.0", extracut=False, extracut_length=0.2,
|
||||
startz=None, endz=2.0, endxy='', pp_geometry_name=None, tool_no=1):
|
||||
"""
|
||||
Algorithm to generate from multitool Geometry.
|
||||
|
||||
|
@ -5025,8 +5022,7 @@ class CNCjob(Geometry):
|
|||
|
||||
return path
|
||||
|
||||
def linear2gcode(self, linear, tolerance=0, down=True, up=True,
|
||||
z_cut=None, z_move=None, zdownrate=None,
|
||||
def linear2gcode(self, linear, tolerance=0, down=True, up=True, z_cut=None, z_move=None, zdownrate=None,
|
||||
feedrate=None, feedrate_z=None, feedrate_rapid=None, cont=False, old_point=(0, 0)):
|
||||
"""
|
||||
|
||||
|
@ -5119,8 +5115,7 @@ class CNCjob(Geometry):
|
|||
# For Incremental coordinates type G91
|
||||
# next_x = pt[0] - prev_x
|
||||
# next_y = pt[1] - prev_y
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_('G91 coordinates not implemented ...'))
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _('G91 coordinates not implemented ...'))
|
||||
next_x = pt[0]
|
||||
next_y = pt[1]
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ class FlatCAMDefaults:
|
|||
"version": 8.992, # defaults format version, not necessarily equal to app version
|
||||
"first_run": True,
|
||||
"units": "MM",
|
||||
"root_folder_path": '',
|
||||
"global_serial": 0,
|
||||
"global_stats": dict(),
|
||||
"global_tabs_detachable": True,
|
||||
|
|
|
@ -2837,7 +2837,7 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
# start with GRID toolbar activated
|
||||
if self.app.ui.grid_snap_btn.isChecked() is False:
|
||||
self.app.ui.grid_snap_btn.trigger()
|
||||
self.app.on_grid_snap_triggered(state=True)
|
||||
self.app.ui.on_grid_snap_triggered(state=True)
|
||||
|
||||
self.app.ui.popmenu_disable.setVisible(False)
|
||||
self.app.ui.cmenu_newmenu.menuAction().setVisible(False)
|
||||
|
@ -3226,7 +3226,7 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
spec = {"C": float(tool_dia[0])}
|
||||
self.new_tools[name] = spec
|
||||
|
||||
# add in self.tools the 'solid_geometry' key, the value (a list) is populated bellow
|
||||
# add in self.tools the 'solid_geometry' key, the value (a list) is populated below
|
||||
self.new_tools[name]['solid_geometry'] = []
|
||||
|
||||
# create the self.drills for the new Excellon object (the one with edited content)
|
||||
|
@ -3258,7 +3258,7 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
spec = {"C": float(tool_dia[0])}
|
||||
self.new_tools[name] = spec
|
||||
|
||||
# add in self.tools the 'solid_geometry' key, the value (a list) is populated bellow
|
||||
# add in self.tools the 'solid_geometry' key, the value (a list) is populated below
|
||||
self.new_tools[name]['solid_geometry'] = []
|
||||
|
||||
# create the self.slots for the new Excellon object (the one with edited content)
|
||||
|
|
|
@ -4100,7 +4100,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
# start with GRID toolbar activated
|
||||
if self.app.ui.grid_snap_btn.isChecked() is False:
|
||||
self.app.ui.grid_snap_btn.trigger()
|
||||
self.app.on_grid_snap_triggered(state=True)
|
||||
self.app.ui.on_grid_snap_triggered(state=True)
|
||||
|
||||
def on_buffer_tool(self):
|
||||
buff_tool = BufferSelectionTool(self.app, self)
|
||||
|
|
|
@ -12,6 +12,8 @@ from shapely.geometry import LineString, LinearRing, MultiLineString, Point, Pol
|
|||
from shapely.ops import cascaded_union
|
||||
import shapely.affinity as affinity
|
||||
|
||||
from vispy.geometry import Rect
|
||||
|
||||
import threading
|
||||
import time
|
||||
from copy import copy, deepcopy
|
||||
|
@ -3701,7 +3703,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
# start with GRID toolbar activated
|
||||
if self.app.ui.grid_snap_btn.isChecked() is False:
|
||||
self.app.ui.grid_snap_btn.trigger()
|
||||
self.app.on_grid_snap_triggered(state=True)
|
||||
self.app.ui.on_grid_snap_triggered(state=True)
|
||||
|
||||
# adjust the visibility of some of the canvas context menu
|
||||
self.app.ui.popmenu_edit.setVisible(False)
|
||||
|
@ -3721,6 +3723,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
except Exception as e:
|
||||
log.debug("FlatCAMGrbEditor.deactivate_grb_editor() --> %s" % str(e))
|
||||
|
||||
self.clear()
|
||||
|
||||
# adjust the status of the menu entries related to the editor
|
||||
self.app.ui.menueditedit.setDisabled(False)
|
||||
self.app.ui.menueditok.setDisabled(True)
|
||||
|
@ -3729,7 +3733,6 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
self.app.ui.popmenu_save.setVisible(False)
|
||||
|
||||
self.disconnect_canvas_event_handlers()
|
||||
self.clear()
|
||||
self.app.ui.grb_edit_toolbar.setDisabled(True)
|
||||
|
||||
settings = QSettings("Open Source", "FlatCAM")
|
||||
|
@ -3937,8 +3940,12 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
pass
|
||||
|
||||
def clear(self):
|
||||
self.thread.quit()
|
||||
|
||||
self.active_tool = None
|
||||
self.selected = []
|
||||
self.storage_dict.clear()
|
||||
self.results.clear()
|
||||
|
||||
self.shapes.clear(update=True)
|
||||
self.tool_shape.clear(update=True)
|
||||
|
@ -3968,7 +3975,16 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
|
||||
file_units = self.gerber_obj.units if self.gerber_obj.units else 'IN'
|
||||
app_units = self.app.defaults['units']
|
||||
self.conversion_factor = 25.4 if file_units == 'IN' else (1 / 25.4) if file_units != app_units else 1
|
||||
# self.conversion_factor = 25.4 if file_units == 'IN' else (1 / 25.4) if file_units != app_units else 1
|
||||
|
||||
if file_units == app_units:
|
||||
self.conversion_factor = 1
|
||||
else:
|
||||
if file_units == 'IN':
|
||||
self.conversion_factor = 25.4
|
||||
else:
|
||||
self.conversion_factor = 0.0393700787401575
|
||||
|
||||
|
||||
# Hide original geometry
|
||||
orig_grb_obj.visible = False
|
||||
|
@ -4228,8 +4244,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
else:
|
||||
new_grb_name = self.edited_obj_name + "_edit"
|
||||
|
||||
self.app.worker_task.emit({'fcn': self.new_edited_gerber,
|
||||
'params': [new_grb_name, self.storage_dict]})
|
||||
self.app.worker_task.emit({'fcn': self.new_edited_gerber, 'params': [new_grb_name, self.storage_dict]})
|
||||
|
||||
@staticmethod
|
||||
def update_options(obj):
|
||||
|
@ -4927,6 +4942,39 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
# self.app.app_cursor.enabled = False
|
||||
# self.app.app_cursor.enabled = True
|
||||
|
||||
def on_zoom_fit(self):
|
||||
"""
|
||||
Callback for zoom-fit request in Gerber Editor
|
||||
|
||||
:return: None
|
||||
"""
|
||||
log.debug("FlatCAMGrbEditor.on_zoom_fit()")
|
||||
|
||||
# calculate all the geometry in the edited Gerber object
|
||||
edit_geo = []
|
||||
for ap_code in self.storage_dict:
|
||||
for geo_el in self.storage_dict[ap_code]['geometry']:
|
||||
actual_geo = geo_el.geo
|
||||
if 'solid' in actual_geo:
|
||||
edit_geo.append(actual_geo['solid'])
|
||||
|
||||
all_geo = cascaded_union(edit_geo)
|
||||
|
||||
# calculate the bounds values for the edited Gerber object
|
||||
xmin, ymin, xmax, ymax = all_geo.bounds
|
||||
|
||||
if self.app.is_legacy is False:
|
||||
new_rect = Rect(xmin, ymin, xmax, ymax)
|
||||
self.app.plotcanvas.fit_view(rect=new_rect)
|
||||
else:
|
||||
width = xmax - xmin
|
||||
height = ymax - ymin
|
||||
xmin -= 0.05 * width
|
||||
xmax += 0.05 * width
|
||||
ymin -= 0.05 * height
|
||||
ymax += 0.05 * height
|
||||
self.app.plotcanvas.adjust_axes(xmin, ymin, xmax, ymax)
|
||||
|
||||
def get_selected(self):
|
||||
"""
|
||||
Returns list of shapes that are selected in the editor.
|
||||
|
|
|
@ -25,8 +25,8 @@ if '_' not in builtins.__dict__:
|
|||
|
||||
class TextEditor(QtWidgets.QWidget):
|
||||
|
||||
def __init__(self, app, text=None, plain_text=None):
|
||||
super().__init__()
|
||||
def __init__(self, app, text=None, plain_text=None, parent=None):
|
||||
super().__init__(parent=parent)
|
||||
|
||||
self.app = app
|
||||
self.plain_text = plain_text
|
||||
|
|
|
@ -2366,7 +2366,6 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
# ########################################################################
|
||||
# ######################## BUILD PREFERENCES #############################
|
||||
# ########################################################################
|
||||
|
||||
self.general_defaults_form = GeneralPreferencesUI(decimals=self.decimals)
|
||||
self.gerber_defaults_form = GerberPreferencesUI(decimals=self.decimals)
|
||||
self.excellon_defaults_form = ExcellonPreferencesUI(decimals=self.decimals)
|
||||
|
@ -2381,7 +2380,6 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
# ########################################################################
|
||||
# ################## RESTORE THE TOOLBAR STATE from file #################
|
||||
# ########################################################################
|
||||
|
||||
flat_settings = QSettings("Open Source", "FlatCAM")
|
||||
if flat_settings.contains("saved_gui_state"):
|
||||
saved_gui_state = flat_settings.value('saved_gui_state')
|
||||
|
@ -2439,15 +2437,42 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
del qsettings
|
||||
|
||||
self.lock_toolbar(lock=lock_state)
|
||||
self.on_grid_snap_triggered(state=True)
|
||||
|
||||
self.lock_action.triggered[bool].connect(self.lock_toolbar)
|
||||
|
||||
self.pref_open_button.clicked.connect(self.on_preferences_open_folder)
|
||||
self.clear_btn.clicked.connect(self.on_gui_clear)
|
||||
self.grid_snap_btn.triggered.connect(self.on_grid_snap_triggered)
|
||||
self.snap_infobar_label.clicked.connect(self.on_grid_icon_snap_clicked)
|
||||
|
||||
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
# %%%%%%%%%%%%%%%%% GUI Building FINISHED %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
def on_grid_snap_triggered(self, state):
|
||||
"""
|
||||
|
||||
:param state: A parameter with the state of the grid, boolean
|
||||
|
||||
:return:
|
||||
"""
|
||||
if state:
|
||||
self.snap_infobar_label.setPixmap(QtGui.QPixmap(self.app.resource_location + '/snap_filled_16.png'))
|
||||
else:
|
||||
self.snap_infobar_label.setPixmap(QtGui.QPixmap(self.app.resource_location + '/snap_16.png'))
|
||||
|
||||
self.snap_infobar_label.clicked_state = state
|
||||
|
||||
def on_grid_icon_snap_clicked(self):
|
||||
"""
|
||||
Slot called by clicking a GUI element, in this case a FCLabel
|
||||
|
||||
:return:
|
||||
"""
|
||||
if isinstance(self.sender(), FCLabel):
|
||||
self.grid_snap_btn.trigger()
|
||||
|
||||
def eventFilter(self, obj, event):
|
||||
"""
|
||||
Filter the ToolTips display based on a Preferences setting
|
||||
|
@ -3207,7 +3232,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
else:
|
||||
self.app.collection.set_active(names_list[active_index-1])
|
||||
|
||||
# Select the object in the Tree bellow the current one
|
||||
# Select the object in the Tree below the current one
|
||||
if key == QtCore.Qt.Key_Down:
|
||||
# make sure it works only for the Project Tab who is an instance of KeySensitiveListView
|
||||
focused_wdg = QtWidgets.QApplication.focusWidget()
|
||||
|
@ -3811,10 +3836,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.app.grb_editor.select_tool('track')
|
||||
return
|
||||
|
||||
# Zoom Fit
|
||||
# Zoom fit
|
||||
if key == QtCore.Qt.Key_V or key == 'V':
|
||||
self.app.grb_editor.launched_from_shortcuts = True
|
||||
self.app.on_zoom_fit(None)
|
||||
self.app.grb_editor.on_zoom_fit()
|
||||
return
|
||||
|
||||
# Show Shortcut list
|
||||
|
|
|
@ -45,7 +45,7 @@ class PreferencesUIManager:
|
|||
# if Preferences are changed in the Edit -> Preferences tab the value will be set to True
|
||||
self.preferences_changed_flag = False
|
||||
|
||||
# when adding entries here read the comments in the method found bellow named:
|
||||
# when adding entries here read the comments in the method found below named:
|
||||
# def new_object(self, kind, name, initialize, active=True, fit=True, plot=True)
|
||||
self.defaults_form_fields = {
|
||||
# General App
|
||||
|
|
|
@ -374,10 +374,25 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
|
|||
self.splash_cb.stateChanged.connect(self.on_splash_changed)
|
||||
|
||||
# Monitor the checkbox from the Application Defaults Tab and show the TCL shell or not depending on it's value
|
||||
self.shell_startup_cb.clicked.connect(self.app.on_toggle_shell_from_settings)
|
||||
self.shell_startup_cb.clicked.connect(self.on_toggle_shell_from_settings)
|
||||
|
||||
self.language_apply_btn.clicked.connect(lambda: fcTranslate.on_language_apply_click(app=self.app, restart=True))
|
||||
|
||||
def on_toggle_shell_from_settings(self, state):
|
||||
"""
|
||||
Toggle shell: if is visible close it, if it is closed then open it
|
||||
:return: None
|
||||
"""
|
||||
|
||||
self.app.defaults.report_usage("on_toggle_shell_from_settings()")
|
||||
|
||||
if state is True:
|
||||
if not self.app.ui.shell_dock.isVisible():
|
||||
self.app.ui.shell_dock.show()
|
||||
else:
|
||||
if self.app.ui.shell_dock.isVisible():
|
||||
self.app.ui.shell_dock.hide()
|
||||
|
||||
@staticmethod
|
||||
def on_splash_changed(state):
|
||||
qsettings = QSettings("Open Source", "FlatCAM")
|
||||
|
|
|
@ -377,7 +377,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI2):
|
|||
self.app.connect_toolbar_signals()
|
||||
|
||||
self.app.ui.grid_snap_btn.setChecked(True)
|
||||
self.app.on_grid_snap_triggered(state=True)
|
||||
self.app.ui.on_grid_snap_triggered(state=True)
|
||||
|
||||
self.app.ui.grid_gap_x_entry.setText(str(self.app.defaults["global_gridx"]))
|
||||
self.app.ui.grid_gap_y_entry.setText(str(self.app.defaults["global_gridy"]))
|
||||
|
|
|
@ -364,7 +364,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
|
|||
self.units_found = self.app.defaults['units']
|
||||
|
||||
# this signal has to be connected to it's slot before the defaults are populated
|
||||
# the decision done in the slot has to override the default value set bellow
|
||||
# the decision done in the slot has to override the default value set below
|
||||
self.ui.toolchange_cb.toggled.connect(self.on_toolchange_custom_clicked)
|
||||
|
||||
self.form_fields.update({
|
||||
|
|
|
@ -1114,6 +1114,26 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||
self.ui.tipanglelabel.show()
|
||||
self.ui.tipangle_entry.show()
|
||||
self.ui.cutz_entry.setDisabled(True)
|
||||
self.ui.cutzlabel.setToolTip(
|
||||
_("Disabled because the tool is V-shape.\n"
|
||||
"For V-shape tools the depth of cut is\n"
|
||||
"calculated from other parameters like:\n"
|
||||
"- 'V-tip Angle' -> angle at the tip of the tool\n"
|
||||
"- 'V-tip Dia' -> diameter at the tip of the tool \n"
|
||||
"- Tool Dia -> 'Dia' column found in the Tool Table\n"
|
||||
"NB: a value of zero means that Tool Dia = 'V-tip Dia'"
|
||||
)
|
||||
)
|
||||
self.ui.cutz_entry.setToolTip(
|
||||
_("Disabled because the tool is V-shape.\n"
|
||||
"For V-shape tools the depth of cut is\n"
|
||||
"calculated from other parameters like:\n"
|
||||
"- 'V-tip Angle' -> angle at the tip of the tool\n"
|
||||
"- 'V-tip Dia' -> diameter at the tip of the tool \n"
|
||||
"- Tool Dia -> 'Dia' column found in the Tool Table\n"
|
||||
"NB: a value of zero means that Tool Dia = 'V-tip Dia'"
|
||||
)
|
||||
)
|
||||
|
||||
self.update_cutz()
|
||||
else:
|
||||
|
@ -1122,6 +1142,12 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||
self.ui.tipanglelabel.hide()
|
||||
self.ui.tipangle_entry.hide()
|
||||
self.ui.cutz_entry.setDisabled(False)
|
||||
self.ui.cutzlabel.setToolTip(
|
||||
_("Cutting depth (negative)\n"
|
||||
"below the copper surface."
|
||||
)
|
||||
)
|
||||
self.ui.cutz_entry.setToolTip('')
|
||||
|
||||
def update_cutz(self):
|
||||
vdia = float(self.ui.tipdia_entry.get_value())
|
||||
|
|
|
@ -50,15 +50,14 @@ class ScriptObject(FlatCAMObj):
|
|||
|
||||
self.units = ''
|
||||
|
||||
self.script_editor_tab = None
|
||||
|
||||
self.ser_attrs = ['options', 'kind', 'source_file']
|
||||
self.source_file = ''
|
||||
self.script_code = ''
|
||||
|
||||
self.units_found = self.app.defaults['units']
|
||||
|
||||
# self.script_editor_tab = TextEditor(app=self.app, plain_text=True)
|
||||
self.script_editor_tab = TextEditor(app=self.app, plain_text=True)
|
||||
|
||||
def set_ui(self, ui):
|
||||
"""
|
||||
Sets the Object UI in Selected Tab for the FlatCAM Script type of object.
|
||||
|
@ -87,6 +86,8 @@ class ScriptObject(FlatCAMObj):
|
|||
'<span style="color:red;"><b>Advanced</b></span>'
|
||||
))
|
||||
|
||||
self.script_editor_tab = TextEditor(app=self.app, plain_text=True, parent=self.app.ui)
|
||||
|
||||
# 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()):
|
||||
|
@ -99,8 +100,8 @@ class ScriptObject(FlatCAMObj):
|
|||
# self.app.ui.plot_tab_area.addTab(self.script_editor_tab, '%s' % _("Script Editor"))
|
||||
# self.script_editor_tab.setObjectName(self.options['name'])
|
||||
|
||||
self.app.ui.plot_tab_area.addTab(self.script_editor_tab, '%s' % _("Script Editor"))
|
||||
self.script_editor_tab.setObjectName(self.options['name'])
|
||||
# self.app.ui.plot_tab_area.addTab(self.script_editor_tab, '%s' % _("Script Editor"))
|
||||
# self.script_editor_tab.setObjectName(self.options['name'])
|
||||
|
||||
# first clear previous text in text editor (if any)
|
||||
# self.script_editor_tab.code_editor.clear()
|
||||
|
@ -111,7 +112,7 @@ class ScriptObject(FlatCAMObj):
|
|||
|
||||
self.script_editor_tab.buttonRun.show()
|
||||
|
||||
# Switch plot_area to CNCJob tab
|
||||
# Switch plot_area to Script Editor tab
|
||||
self.app.ui.plot_tab_area.setCurrentWidget(self.script_editor_tab)
|
||||
|
||||
flt = "FlatCAM Scripts (*.FlatScript);;All Files (*.*)"
|
||||
|
@ -150,6 +151,32 @@ class ScriptObject(FlatCAMObj):
|
|||
def build_ui(self):
|
||||
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'])
|
||||
self.app.ui.plot_tab_area.setCurrentWidget(self.script_editor_tab)
|
||||
|
||||
def parse_file(self, filename):
|
||||
"""
|
||||
Will set an attribute of the object, self.source_file, with the parsed data.
|
||||
|
||||
:param filename: Tcl Script file to parse
|
||||
:return: None
|
||||
"""
|
||||
with open(filename, "r") as opened_script:
|
||||
script_content = opened_script.readlines()
|
||||
script_content = ''.join(script_content)
|
||||
|
||||
self.source_file = script_content
|
||||
|
||||
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
|
||||
|
|
|
@ -426,7 +426,7 @@ class Excellon(Geometry):
|
|||
# it's possible that tool definition has only tool number and no diameter info
|
||||
# (those could be in another file like PCB Wizard do)
|
||||
# then match.group(2) = None and float(None) will create the exception
|
||||
# the bellow construction is so each tool will have a slightly different diameter
|
||||
# the below construction is so each tool will have a slightly different diameter
|
||||
# starting with a default value, to allow Excellon editing after that
|
||||
self.diameterless = True
|
||||
self.app.inform.emit('[WARNING] %s%s %s' %
|
||||
|
|
|
@ -293,12 +293,12 @@ class CutOut(FlatCAMTool):
|
|||
font-weight: bold;
|
||||
}
|
||||
""")
|
||||
self.layout.addWidget(self.rect_cutout_object_btn)
|
||||
grid0.addWidget(self.rect_cutout_object_btn, 21, 0, 1, 2)
|
||||
|
||||
separator_line = QtWidgets.QFrame()
|
||||
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
grid0.addWidget(separator_line, 21, 0, 1, 2)
|
||||
grid0.addWidget(separator_line, 22, 0, 1, 2)
|
||||
|
||||
# Title5
|
||||
title_manual_label = QtWidgets.QLabel("<font size=4><b>%s</b></font>" % _('B. Manual Bridge Gaps'))
|
||||
|
@ -307,7 +307,7 @@ class CutOut(FlatCAMTool):
|
|||
"This is done by mouse clicking on the perimeter of the\n"
|
||||
"Geometry object that is used as a cutout object. ")
|
||||
)
|
||||
grid0.addWidget(title_manual_label, 22, 0, 1, 2)
|
||||
grid0.addWidget(title_manual_label, 23, 0, 1, 2)
|
||||
|
||||
# Manual Geo Object
|
||||
self.man_object_combo = FCComboBox()
|
||||
|
@ -322,8 +322,8 @@ class CutOut(FlatCAMTool):
|
|||
)
|
||||
# self.man_object_label.setMinimumWidth(60)
|
||||
|
||||
grid0.addWidget(self.man_object_label, 23, 0, 1, 2)
|
||||
grid0.addWidget(self.man_object_combo, 24, 0, 1, 2)
|
||||
grid0.addWidget(self.man_object_label, 25, 0, 1, 2)
|
||||
grid0.addWidget(self.man_object_combo, 26, 0, 1, 2)
|
||||
|
||||
self.man_geo_creation_btn = FCButton(_("Generate Manual Geometry"))
|
||||
self.man_geo_creation_btn.setToolTip(
|
||||
|
@ -338,7 +338,7 @@ class CutOut(FlatCAMTool):
|
|||
font-weight: bold;
|
||||
}
|
||||
""")
|
||||
grid0.addWidget(self.man_geo_creation_btn, 25, 0, 1, 2)
|
||||
grid0.addWidget(self.man_geo_creation_btn, 28, 0, 1, 2)
|
||||
|
||||
self.man_gaps_creation_btn = FCButton(_("Manual Add Bridge Gaps"))
|
||||
self.man_gaps_creation_btn.setToolTip(
|
||||
|
@ -354,7 +354,7 @@ class CutOut(FlatCAMTool):
|
|||
font-weight: bold;
|
||||
}
|
||||
""")
|
||||
grid0.addWidget(self.man_gaps_creation_btn, 27, 0, 1, 2)
|
||||
grid0.addWidget(self.man_gaps_creation_btn, 30, 0, 1, 2)
|
||||
|
||||
self.layout.addStretch()
|
||||
|
||||
|
@ -394,6 +394,9 @@ class CutOut(FlatCAMTool):
|
|||
self.x_pos = None
|
||||
self.y_pos = None
|
||||
|
||||
# store the default data for the resulting Geometry Object
|
||||
self.default_data = {}
|
||||
|
||||
# Signals
|
||||
self.ff_cutout_object_btn.clicked.connect(self.on_freeform_cutout)
|
||||
self.rect_cutout_object_btn.clicked.connect(self.on_rectangular_cutout)
|
||||
|
@ -454,6 +457,48 @@ class CutOut(FlatCAMTool):
|
|||
self.convex_box.set_value(self.app.defaults['tools_cutout_convexshape'])
|
||||
self.type_obj_radio.set_value('grb')
|
||||
|
||||
self.default_data.update({
|
||||
"plot": True,
|
||||
"cutz": float(self.app.defaults["geometry_cutz"]),
|
||||
"multidepth": self.app.defaults["geometry_multidepth"],
|
||||
"depthperpass": float(self.app.defaults["geometry_depthperpass"]),
|
||||
"vtipdia": float(self.app.defaults["geometry_vtipdia"]),
|
||||
"vtipangle": float(self.app.defaults["geometry_vtipangle"]),
|
||||
"travelz": float(self.app.defaults["geometry_travelz"]),
|
||||
"feedrate": float(self.app.defaults["geometry_feedrate"]),
|
||||
"feedrate_z": float(self.app.defaults["geometry_feedrate_z"]),
|
||||
"feedrate_rapid": float(self.app.defaults["geometry_feedrate_rapid"]),
|
||||
"spindlespeed": self.app.defaults["geometry_spindlespeed"],
|
||||
"dwell": self.app.defaults["geometry_dwell"],
|
||||
"dwelltime": float(self.app.defaults["geometry_dwelltime"]),
|
||||
"ppname_g": self.app.defaults["geometry_ppname_g"],
|
||||
"extracut": self.app.defaults["geometry_extracut"],
|
||||
"extracut_length": float(self.app.defaults["geometry_extracut_length"]),
|
||||
"toolchange": self.app.defaults["geometry_toolchange"],
|
||||
"toolchangexy": self.app.defaults["geometry_toolchangexy"],
|
||||
"toolchangez": float(self.app.defaults["geometry_toolchangez"]),
|
||||
"startz": self.app.defaults["geometry_startz"],
|
||||
"endz": float(self.app.defaults["geometry_endz"]),
|
||||
|
||||
# NCC
|
||||
"tools_nccoperation": self.app.defaults["tools_nccoperation"],
|
||||
"tools_nccmilling_type": self.app.defaults["tools_nccmilling_type"],
|
||||
"tools_nccoverlap": float(self.app.defaults["tools_nccoverlap"]),
|
||||
"tools_nccmargin": float(self.app.defaults["tools_nccmargin"]),
|
||||
"tools_nccmethod": self.app.defaults["tools_nccmethod"],
|
||||
"tools_nccconnect": self.app.defaults["tools_nccconnect"],
|
||||
"tools_ncccontour": self.app.defaults["tools_ncccontour"],
|
||||
"tools_ncc_offset_choice": self.app.defaults["tools_ncc_offset_choice"],
|
||||
"tools_ncc_offset_value": float(self.app.defaults["tools_ncc_offset_value"]),
|
||||
|
||||
# Paint
|
||||
"tools_paintoverlap": float(self.app.defaults["tools_paintoverlap"]),
|
||||
"tools_paintmargin": float(self.app.defaults["tools_paintmargin"]),
|
||||
"tools_paintmethod": self.app.defaults["tools_paintmethod"],
|
||||
"tools_pathconnect": self.app.defaults["tools_pathconnect"],
|
||||
"tools_paintcontour": self.app.defaults["tools_paintcontour"],
|
||||
})
|
||||
|
||||
def on_freeform_cutout(self):
|
||||
log.debug("Cutout.on_freeform_cutout() was launched ...")
|
||||
|
||||
|
@ -622,8 +667,8 @@ class CutOut(FlatCAMTool):
|
|||
|
||||
solid_geo += cutout_handler(geom=geom_struct)
|
||||
|
||||
geo_obj.solid_geometry = deepcopy(solid_geo)
|
||||
xmin, ymin, xmax, ymax = recursive_bounds(geo_obj.solid_geometry)
|
||||
geo_obj.solid_geometry = deepcopy(solid_geo)
|
||||
geo_obj.options['xmin'] = xmin
|
||||
geo_obj.options['ymin'] = ymin
|
||||
geo_obj.options['xmax'] = xmax
|
||||
|
@ -633,6 +678,23 @@ class CutOut(FlatCAMTool):
|
|||
geo_obj.options['multidepth'] = self.mpass_cb.get_value()
|
||||
geo_obj.options['depthperpass'] = self.maxdepth_entry.get_value()
|
||||
|
||||
geo_obj.tools.update({
|
||||
1: {
|
||||
'tooldia': str(dia),
|
||||
'offset': 'Path',
|
||||
'offset_value': 0.0,
|
||||
'type': _('Rough'),
|
||||
'tool_type': 'C1',
|
||||
'data': self.default_data,
|
||||
'solid_geometry': geo_obj.solid_geometry
|
||||
}
|
||||
})
|
||||
geo_obj.multigeo = True
|
||||
geo_obj.tools[1]['data']['name'] = outname
|
||||
geo_obj.tools[1]['data']['cutz'] = self.cutz_entry.get_value()
|
||||
geo_obj.tools[1]['data']['multidepth'] = self.mpass_cb.get_value()
|
||||
geo_obj.tools[1]['data']['depthperpass'] = self.maxdepth_entry.get_value()
|
||||
|
||||
outname = cutout_obj.options["name"] + "_cutout"
|
||||
self.app.new_object('geometry', outname, geo_init)
|
||||
|
||||
|
@ -759,6 +821,7 @@ class CutOut(FlatCAMTool):
|
|||
return proc_geometry
|
||||
|
||||
if kind == 'single':
|
||||
# fuse the lines
|
||||
object_geo = unary_union(object_geo)
|
||||
|
||||
xmin, ymin, xmax, ymax = object_geo.bounds
|
||||
|
@ -805,11 +868,28 @@ class CutOut(FlatCAMTool):
|
|||
_("Rectangular cutout with negative margin is not possible."))
|
||||
return "fail"
|
||||
|
||||
geo_obj.solid_geometry = deepcopy(solid_geo)
|
||||
geo_obj.options['cnctooldia'] = str(dia)
|
||||
geo_obj.options['cutz'] = self.cutz_entry.get_value()
|
||||
geo_obj.options['multidepth'] = self.mpass_cb.get_value()
|
||||
geo_obj.options['depthperpass'] = self.maxdepth_entry.get_value()
|
||||
geo_obj.solid_geometry = deepcopy(solid_geo)
|
||||
|
||||
geo_obj.tools.update({
|
||||
1: {
|
||||
'tooldia': str(dia),
|
||||
'offset': 'Path',
|
||||
'offset_value': 0.0,
|
||||
'type': _('Rough'),
|
||||
'tool_type': 'C1',
|
||||
'data': self.default_data,
|
||||
'solid_geometry': geo_obj.solid_geometry
|
||||
}
|
||||
})
|
||||
geo_obj.multigeo = True
|
||||
geo_obj.tools[1]['data']['name'] = outname
|
||||
geo_obj.tools[1]['data']['cutz'] = self.cutz_entry.get_value()
|
||||
geo_obj.tools[1]['data']['multidepth'] = self.mpass_cb.get_value()
|
||||
geo_obj.tools[1]['data']['depthperpass'] = self.maxdepth_entry.get_value()
|
||||
|
||||
outname = cutout_obj.options["name"] + "_cutout"
|
||||
ret = self.app.new_object('geometry', outname, geo_init)
|
||||
|
@ -954,6 +1034,23 @@ class CutOut(FlatCAMTool):
|
|||
geo_obj.options['multidepth'] = self.mpass_cb.get_value()
|
||||
geo_obj.options['depthperpass'] = self.maxdepth_entry.get_value()
|
||||
|
||||
geo_obj.tools.update({
|
||||
1: {
|
||||
'tooldia': str(dia),
|
||||
'offset': 'Path',
|
||||
'offset_value': 0.0,
|
||||
'type': _('Rough'),
|
||||
'tool_type': 'C1',
|
||||
'data': self.default_data,
|
||||
'solid_geometry': geo_obj.solid_geometry
|
||||
}
|
||||
})
|
||||
geo_obj.multigeo = True
|
||||
geo_obj.tools[1]['data']['name'] = outname
|
||||
geo_obj.tools[1]['data']['cutz'] = self.cutz_entry.get_value()
|
||||
geo_obj.tools[1]['data']['multidepth'] = self.mpass_cb.get_value()
|
||||
geo_obj.tools[1]['data']['depthperpass'] = self.maxdepth_entry.get_value()
|
||||
|
||||
outname = cutout_obj.options["name"] + "_cutout"
|
||||
self.app.new_object('geometry', outname, geo_init)
|
||||
|
||||
|
|
|
@ -128,7 +128,7 @@ class Film(FlatCAMTool):
|
|||
|
||||
self.tf_box_combo_label = QtWidgets.QLabel('%s:' % _("Box Object"))
|
||||
self.tf_box_combo_label.setToolTip(
|
||||
_("The actual object that is used a container for the\n "
|
||||
_("The actual object that is used as container for the\n "
|
||||
"selected object for which we create the film.\n"
|
||||
"Usually it is the PCB outline but it can be also the\n"
|
||||
"same object for which the film is created.")
|
||||
|
|
|
@ -14,6 +14,8 @@ from copy import deepcopy
|
|||
import numpy as np
|
||||
|
||||
import shapely.affinity as affinity
|
||||
from shapely.ops import unary_union
|
||||
from shapely.geometry import LineString
|
||||
|
||||
import gettext
|
||||
import FlatCAMTranslation as fcTranslate
|
||||
|
@ -136,7 +138,7 @@ class Panelize(FlatCAMTool):
|
|||
self.box_combo.is_last = True
|
||||
|
||||
self.box_combo.setToolTip(
|
||||
_("The actual object that is used a container for the\n "
|
||||
_("The actual object that is used as container for the\n "
|
||||
"selected object that is to be panelized.")
|
||||
)
|
||||
form_layout.addRow(self.box_combo)
|
||||
|
@ -402,19 +404,18 @@ class Panelize(FlatCAMTool):
|
|||
def on_panelize(self):
|
||||
name = self.object_combo.currentText()
|
||||
|
||||
# Get source object.
|
||||
# Get source object to be panelized.
|
||||
try:
|
||||
panel_obj = self.app.collection.get_by_name(str(name))
|
||||
panel_source_obj = self.app.collection.get_by_name(str(name))
|
||||
except Exception as e:
|
||||
log.debug("Panelize.on_panelize() --> %s" % str(e))
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
|
||||
(_("Could not retrieve object"), name))
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), name))
|
||||
return "Could not retrieve object: %s" % name
|
||||
|
||||
if panel_obj is None:
|
||||
if panel_source_obj is None:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
|
||||
(_("Object not found"), panel_obj))
|
||||
return "Object not found: %s" % panel_obj
|
||||
(_("Object not found"), panel_source_obj))
|
||||
return "Object not found: %s" % panel_source_obj
|
||||
|
||||
boxname = self.box_combo.currentText()
|
||||
|
||||
|
@ -422,17 +423,15 @@ class Panelize(FlatCAMTool):
|
|||
box = self.app.collection.get_by_name(boxname)
|
||||
except Exception as e:
|
||||
log.debug("Panelize.on_panelize() --> %s" % str(e))
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
|
||||
(_("Could not retrieve object"), boxname))
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), boxname))
|
||||
return "Could not retrieve object: %s" % boxname
|
||||
|
||||
if box is None:
|
||||
self.app.inform.emit('[WARNING_NOTCL]%s: %s' %
|
||||
(_("No object Box. Using instead"), panel_obj))
|
||||
self.app.inform.emit('[WARNING_NOTCL]%s: %s' % (_("No object Box. Using instead"), panel_source_obj))
|
||||
self.reference_radio.set_value('bbox')
|
||||
|
||||
if self.reference_radio.get_value() == 'bbox':
|
||||
box = panel_obj
|
||||
box = panel_source_obj
|
||||
|
||||
self.outname = name + '_panelized'
|
||||
|
||||
|
@ -478,20 +477,20 @@ class Panelize(FlatCAMTool):
|
|||
rows -= 1
|
||||
panel_lengthy = ((ymax - ymin) * rows) + (spacing_rows * (rows - 1))
|
||||
|
||||
if panel_obj.kind == 'excellon' or panel_obj.kind == 'geometry':
|
||||
if panel_source_obj.kind == 'excellon' or panel_source_obj.kind == 'geometry':
|
||||
# make a copy of the panelized Excellon or Geometry tools
|
||||
copied_tools = {}
|
||||
for tt, tt_val in list(panel_obj.tools.items()):
|
||||
for tt, tt_val in list(panel_source_obj.tools.items()):
|
||||
copied_tools[tt] = deepcopy(tt_val)
|
||||
|
||||
if panel_obj.kind == 'gerber':
|
||||
if panel_source_obj.kind == 'gerber':
|
||||
# make a copy of the panelized Gerber apertures
|
||||
copied_apertures = {}
|
||||
for tt, tt_val in list(panel_obj.apertures.items()):
|
||||
for tt, tt_val in list(panel_source_obj.apertures.items()):
|
||||
copied_apertures[tt] = deepcopy(tt_val)
|
||||
|
||||
def panelize_2():
|
||||
if panel_obj is not None:
|
||||
def panelize_worker():
|
||||
if panel_source_obj is not None:
|
||||
self.app.inform.emit(_("Generating panel ... "))
|
||||
|
||||
def job_init_excellon(obj_fin, app_obj):
|
||||
|
@ -501,15 +500,15 @@ class Panelize(FlatCAMTool):
|
|||
obj_fin.slots = []
|
||||
obj_fin.solid_geometry = []
|
||||
|
||||
for option in panel_obj.options:
|
||||
for option in panel_source_obj.options:
|
||||
if option != 'name':
|
||||
try:
|
||||
obj_fin.options[option] = panel_obj.options[option]
|
||||
obj_fin.options[option] = panel_source_obj.options[option]
|
||||
except KeyError:
|
||||
log.warning("Failed to copy option. %s" % str(option))
|
||||
|
||||
geo_len_drills = len(panel_obj.drills) if panel_obj.drills else 0
|
||||
geo_len_slots = len(panel_obj.slots) if panel_obj.slots else 0
|
||||
geo_len_drills = len(panel_source_obj.drills) if panel_source_obj.drills else 0
|
||||
geo_len_slots = len(panel_source_obj.slots) if panel_source_obj.slots else 0
|
||||
|
||||
element = 0
|
||||
for row in range(rows):
|
||||
|
@ -518,9 +517,9 @@ class Panelize(FlatCAMTool):
|
|||
element += 1
|
||||
old_disp_number = 0
|
||||
|
||||
if panel_obj.drills:
|
||||
if panel_source_obj.drills:
|
||||
drill_nr = 0
|
||||
for tool_dict in panel_obj.drills:
|
||||
for tool_dict in panel_source_obj.drills:
|
||||
if self.app.abort_flag:
|
||||
# graceful abort requested by the user
|
||||
raise grace
|
||||
|
@ -543,9 +542,9 @@ class Panelize(FlatCAMTool):
|
|||
disp_number))
|
||||
old_disp_number = disp_number
|
||||
|
||||
if panel_obj.slots:
|
||||
if panel_source_obj.slots:
|
||||
slot_nr = 0
|
||||
for tool_dict in panel_obj.slots:
|
||||
for tool_dict in panel_source_obj.slots:
|
||||
if self.app.abort_flag:
|
||||
# graceful abort requested by the user
|
||||
raise grace
|
||||
|
@ -574,8 +573,8 @@ class Panelize(FlatCAMTool):
|
|||
currenty += lenghty
|
||||
|
||||
obj_fin.create_geometry()
|
||||
obj_fin.zeros = panel_obj.zeros
|
||||
obj_fin.units = panel_obj.units
|
||||
obj_fin.zeros = panel_source_obj.zeros
|
||||
obj_fin.units = panel_source_obj.units
|
||||
self.app.proc_container.update_view_text('')
|
||||
|
||||
def job_init_geometry(obj_fin, app_obj):
|
||||
|
@ -598,36 +597,36 @@ class Panelize(FlatCAMTool):
|
|||
obj_fin.solid_geometry = []
|
||||
|
||||
# create the initial structure on which to create the panel
|
||||
if panel_obj.kind == 'geometry':
|
||||
obj_fin.multigeo = panel_obj.multigeo
|
||||
if panel_source_obj.kind == 'geometry':
|
||||
obj_fin.multigeo = panel_source_obj.multigeo
|
||||
obj_fin.tools = copied_tools
|
||||
if panel_obj.multigeo is True:
|
||||
for tool in panel_obj.tools:
|
||||
if panel_source_obj.multigeo is True:
|
||||
for tool in panel_source_obj.tools:
|
||||
obj_fin.tools[tool]['solid_geometry'][:] = []
|
||||
elif panel_obj.kind == 'gerber':
|
||||
elif panel_source_obj.kind == 'gerber':
|
||||
obj_fin.apertures = copied_apertures
|
||||
for ap in obj_fin.apertures:
|
||||
obj_fin.apertures[ap]['geometry'] = []
|
||||
|
||||
# find the number of polygons in the source solid_geometry
|
||||
geo_len = 0
|
||||
if panel_obj.kind == 'geometry':
|
||||
if panel_obj.multigeo is True:
|
||||
for tool in panel_obj.tools:
|
||||
if panel_source_obj.kind == 'geometry':
|
||||
if panel_source_obj.multigeo is True:
|
||||
for tool in panel_source_obj.tools:
|
||||
try:
|
||||
geo_len += len(panel_obj.tools[tool]['solid_geometry'])
|
||||
geo_len += len(panel_source_obj.tools[tool]['solid_geometry'])
|
||||
except TypeError:
|
||||
geo_len += 1
|
||||
else:
|
||||
try:
|
||||
geo_len = len(panel_obj.solid_geometry)
|
||||
geo_len = len(panel_source_obj.solid_geometry)
|
||||
except TypeError:
|
||||
geo_len = 1
|
||||
elif panel_obj.kind == 'gerber':
|
||||
for ap in panel_obj.apertures:
|
||||
if 'geometry' in panel_obj.apertures[ap]:
|
||||
elif panel_source_obj.kind == 'gerber':
|
||||
for ap in panel_source_obj.apertures:
|
||||
if 'geometry' in panel_source_obj.apertures[ap]:
|
||||
try:
|
||||
geo_len += len(panel_obj.apertures[ap]['geometry'])
|
||||
geo_len += len(panel_source_obj.apertures[ap]['geometry'])
|
||||
except TypeError:
|
||||
geo_len += 1
|
||||
|
||||
|
@ -639,29 +638,23 @@ class Panelize(FlatCAMTool):
|
|||
element += 1
|
||||
old_disp_number = 0
|
||||
|
||||
if panel_obj.kind == 'geometry':
|
||||
if panel_obj.multigeo is True:
|
||||
for tool in panel_obj.tools:
|
||||
# Will panelize a Geometry Object
|
||||
if panel_source_obj.kind == 'geometry':
|
||||
if panel_source_obj.multigeo is True:
|
||||
for tool in panel_source_obj.tools:
|
||||
if self.app.abort_flag:
|
||||
# graceful abort requested by the user
|
||||
raise grace
|
||||
|
||||
# geo = translate_recursion(panel_obj.tools[tool]['solid_geometry'])
|
||||
# if isinstance(geo, list):
|
||||
# obj_fin.tools[tool]['solid_geometry'] += geo
|
||||
# else:
|
||||
# obj_fin.tools[tool]['solid_geometry'].append(geo)
|
||||
|
||||
# calculate the number of polygons
|
||||
geo_len = len(panel_obj.tools[tool]['solid_geometry'])
|
||||
geo_len = len(panel_source_obj.tools[tool]['solid_geometry'])
|
||||
pol_nr = 0
|
||||
for geo_el in panel_obj.tools[tool]['solid_geometry']:
|
||||
for geo_el in panel_source_obj.tools[tool]['solid_geometry']:
|
||||
trans_geo = translate_recursion(geo_el)
|
||||
obj_fin.tools[tool]['solid_geometry'].append(trans_geo)
|
||||
|
||||
pol_nr += 1
|
||||
disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
|
||||
|
||||
if old_disp_number < disp_number <= 100:
|
||||
self.app.proc_container.update_view_text(' %s: %d %d%%' %
|
||||
(_("Copy"),
|
||||
|
@ -669,23 +662,18 @@ class Panelize(FlatCAMTool):
|
|||
disp_number))
|
||||
old_disp_number = disp_number
|
||||
else:
|
||||
# geo = translate_recursion(panel_obj.solid_geometry)
|
||||
# if isinstance(geo, list):
|
||||
# obj_fin.solid_geometry += geo
|
||||
# else:
|
||||
# obj_fin.solid_geometry.append(geo)
|
||||
if self.app.abort_flag:
|
||||
# graceful abort requested by the user
|
||||
raise grace
|
||||
|
||||
try:
|
||||
# calculate the number of polygons
|
||||
geo_len = len(panel_obj.solid_geometry)
|
||||
geo_len = len(panel_source_obj.solid_geometry)
|
||||
except TypeError:
|
||||
geo_len = 1
|
||||
pol_nr = 0
|
||||
try:
|
||||
for geo_el in panel_obj.solid_geometry:
|
||||
for geo_el in panel_source_obj.solid_geometry:
|
||||
if self.app.abort_flag:
|
||||
# graceful abort requested by the user
|
||||
raise grace
|
||||
|
@ -702,21 +690,18 @@ class Panelize(FlatCAMTool):
|
|||
int(element),
|
||||
disp_number))
|
||||
old_disp_number = disp_number
|
||||
|
||||
except TypeError:
|
||||
trans_geo = translate_recursion(panel_obj.solid_geometry)
|
||||
trans_geo = translate_recursion(panel_source_obj.solid_geometry)
|
||||
obj_fin.solid_geometry.append(trans_geo)
|
||||
# Will panelize a Gerber Object
|
||||
else:
|
||||
# geo = translate_recursion(panel_obj.solid_geometry)
|
||||
# if isinstance(geo, list):
|
||||
# obj_fin.solid_geometry += geo
|
||||
# else:
|
||||
# obj_fin.solid_geometry.append(geo)
|
||||
if self.app.abort_flag:
|
||||
# graceful abort requested by the user
|
||||
raise grace
|
||||
|
||||
try:
|
||||
for geo_el in panel_obj.solid_geometry:
|
||||
for geo_el in panel_source_obj.solid_geometry:
|
||||
if self.app.abort_flag:
|
||||
# graceful abort requested by the user
|
||||
raise grace
|
||||
|
@ -724,21 +709,21 @@ class Panelize(FlatCAMTool):
|
|||
trans_geo = translate_recursion(geo_el)
|
||||
obj_fin.solid_geometry.append(trans_geo)
|
||||
except TypeError:
|
||||
trans_geo = translate_recursion(panel_obj.solid_geometry)
|
||||
trans_geo = translate_recursion(panel_source_obj.solid_geometry)
|
||||
obj_fin.solid_geometry.append(trans_geo)
|
||||
|
||||
for apid in panel_obj.apertures:
|
||||
for apid in panel_source_obj.apertures:
|
||||
if self.app.abort_flag:
|
||||
# graceful abort requested by the user
|
||||
raise grace
|
||||
if 'geometry' in panel_obj.apertures[apid]:
|
||||
if 'geometry' in panel_source_obj.apertures[apid]:
|
||||
try:
|
||||
# calculate the number of polygons
|
||||
geo_len = len(panel_obj.apertures[apid]['geometry'])
|
||||
geo_len = len(panel_source_obj.apertures[apid]['geometry'])
|
||||
except TypeError:
|
||||
geo_len = 1
|
||||
pol_nr = 0
|
||||
for el in panel_obj.apertures[apid]['geometry']:
|
||||
for el in panel_source_obj.apertures[apid]['geometry']:
|
||||
if self.app.abort_flag:
|
||||
# graceful abort requested by the user
|
||||
raise grace
|
||||
|
@ -771,20 +756,34 @@ class Panelize(FlatCAMTool):
|
|||
currentx += lenghtx
|
||||
currenty += lenghty
|
||||
|
||||
print("before", obj_fin.tools)
|
||||
if panel_source_obj.kind == 'geometry' and panel_source_obj.multigeo is True:
|
||||
# I'm going to do this only here as a fix for panelizing cutouts
|
||||
# I'm going to separate linestrings out of the solid geometry from other
|
||||
# possible type of elements and apply unary_union on them to fuse them
|
||||
for tool in obj_fin.tools:
|
||||
lines = []
|
||||
other_geo = []
|
||||
for geo in obj_fin.tools[tool]['solid_geometry']:
|
||||
if isinstance(geo, LineString):
|
||||
lines.append(geo)
|
||||
else:
|
||||
other_geo.append(geo)
|
||||
fused_lines = list(unary_union(lines))
|
||||
obj_fin.tools[tool]['solid_geometry'] = fused_lines + other_geo
|
||||
print("after", obj_fin.tools)
|
||||
|
||||
if panel_type == 'gerber':
|
||||
self.app.inform.emit('%s' % _("Generating panel ... Adding the Gerber code."))
|
||||
obj_fin.source_file = self.app.export_gerber(obj_name=self.outname, filename=None,
|
||||
local_use=obj_fin, use_thread=False)
|
||||
|
||||
# app_obj.log.debug("Found %s geometries. Creating a panel geometry cascaded union ..." %
|
||||
# len(obj_fin.solid_geometry))
|
||||
|
||||
# obj_fin.solid_geometry = cascaded_union(obj_fin.solid_geometry)
|
||||
# app_obj.log.debug("Finished creating a cascaded union for the panel.")
|
||||
self.app.proc_container.update_view_text('')
|
||||
|
||||
self.app.inform.emit('%s: %d' % (_("Generating panel... Spawning copies"), (int(rows * columns))))
|
||||
if panel_obj.kind == 'excellon':
|
||||
if panel_source_obj.kind == 'excellon':
|
||||
self.app.new_object("excellon", self.outname, job_init_excellon, plot=True, autoselected=True)
|
||||
else:
|
||||
self.app.new_object(panel_type, self.outname, job_init_geometry, plot=True, autoselected=True)
|
||||
|
@ -801,7 +800,7 @@ class Panelize(FlatCAMTool):
|
|||
|
||||
def job_thread(app_obj):
|
||||
try:
|
||||
panelize_2()
|
||||
panelize_worker()
|
||||
self.app.inform.emit('[success] %s' % _("Panel created successfully."))
|
||||
except Exception as ee:
|
||||
proc.done()
|
||||
|
|
|
@ -421,8 +421,7 @@ class PcbWizard(FlatCAMTool):
|
|||
ret = excellon_obj.parse_file(file_obj=excellon_fileobj)
|
||||
if ret == "fail":
|
||||
app_obj.log.debug("Excellon parsing failed.")
|
||||
app_obj.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_("This is not Excellon file."))
|
||||
app_obj.inform.emit('[ERROR_NOTCL] %s' % _("This is not Excellon file."))
|
||||
return "fail"
|
||||
except IOError:
|
||||
app_obj.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Cannot parse file"), self.outname))
|
||||
|
@ -443,8 +442,7 @@ class PcbWizard(FlatCAMTool):
|
|||
for tool in excellon_obj.tools:
|
||||
if excellon_obj.tools[tool]['solid_geometry']:
|
||||
return
|
||||
app_obj.inform.emit('[ERROR_NOTCL] %s: %s' %
|
||||
(_("No geometry found in file"), name))
|
||||
app_obj.inform.emit('[ERROR_NOTCL] %s: %s' % (_("No geometry found in file"), name))
|
||||
return "fail"
|
||||
|
||||
if excellon_fileobj is not None and excellon_fileobj != '':
|
||||
|
@ -463,12 +461,9 @@ class PcbWizard(FlatCAMTool):
|
|||
self.app.file_opened.emit("excellon", name)
|
||||
|
||||
# GUI feedback
|
||||
self.app.inform.emit('[success] %s: %s' %
|
||||
(_("Imported"), name))
|
||||
self.app.inform.emit('[success] %s: %s' % (_("Imported"), name))
|
||||
self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
|
||||
else:
|
||||
self.app.inform.emit('[WARNING_NOTCL] %s' %
|
||||
_('Excellon merging is in progress. Please wait...'))
|
||||
self.app.inform.emit('[WARNING_NOTCL] %s' % _('Excellon merging is in progress. Please wait...'))
|
||||
else:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_('The imported Excellon file is None.'))
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _('The imported Excellon file is empty.'))
|
||||
|
|
|
@ -155,7 +155,7 @@ class SolderPaste(FlatCAMTool):
|
|||
step1_lbl = QtWidgets.QLabel("<b>%s:</b>" % _('STEP 1'))
|
||||
step1_lbl.setToolTip(
|
||||
_("First step is to select a number of nozzle tools for usage\n"
|
||||
"and then optionally modify the GCode parameters bellow.")
|
||||
"and then optionally modify the GCode parameters below.")
|
||||
)
|
||||
step1_description_lbl = QtWidgets.QLabel(_("Select tools.\n"
|
||||
"Modify parameters."))
|
||||
|
|
|
@ -97,7 +97,7 @@ class ToolSub(FlatCAMTool):
|
|||
|
||||
form_layout.addRow(self.sub_gerber_label, self.sub_gerber_combo)
|
||||
|
||||
self.intersect_btn = FCButton(_('Substract Gerber'))
|
||||
self.intersect_btn = FCButton(_('Subtract Gerber'))
|
||||
self.intersect_btn.setToolTip(
|
||||
_("Will remove the area occupied by the subtractor\n"
|
||||
"Gerber from the Target Gerber.\n"
|
||||
|
|
|
@ -6789,7 +6789,7 @@ msgstr ""
|
|||
#: flatcamGUI/ObjectUI.py:436
|
||||
msgid ""
|
||||
"When the isolation geometry is generated,\n"
|
||||
"by checking this, the area of the object bellow\n"
|
||||
"by checking this, the area of the object below\n"
|
||||
"will be subtracted from the isolation geometry."
|
||||
msgstr ""
|
||||
|
||||
|
@ -13014,7 +13014,7 @@ msgstr ""
|
|||
|
||||
#: flatcamTools/ToolFilm.py:131
|
||||
msgid ""
|
||||
"The actual object that is used a container for the\n"
|
||||
"The actual object that is used as container for the\n"
|
||||
" selected object for which we create the film.\n"
|
||||
"Usually it is the PCB outline but it can be also the\n"
|
||||
"same object for which the film is created."
|
||||
|
@ -14116,7 +14116,7 @@ msgid "Excellon merging is in progress. Please wait..."
|
|||
msgstr ""
|
||||
|
||||
#: flatcamTools/ToolPcbWizard.py:474
|
||||
msgid "The imported Excellon file is None."
|
||||
msgid "The imported Excellon file is empty."
|
||||
msgstr ""
|
||||
|
||||
#: flatcamTools/ToolProperties.py:131
|
||||
|
@ -14604,7 +14604,7 @@ msgstr ""
|
|||
#: flatcamTools/ToolSolderPaste.py:158
|
||||
msgid ""
|
||||
"First step is to select a number of nozzle tools for usage\n"
|
||||
"and then optionally modify the GCode parameters bellow."
|
||||
"and then optionally modify the GCode parameters below."
|
||||
msgstr ""
|
||||
|
||||
#: flatcamTools/ToolSolderPaste.py:161
|
||||
|
@ -14800,7 +14800,7 @@ msgid ""
|
|||
msgstr ""
|
||||
|
||||
#: flatcamTools/ToolSub.py:100
|
||||
msgid "Substract Gerber"
|
||||
msgid "Subtract Gerber"
|
||||
msgstr ""
|
||||
|
||||
#: flatcamTools/ToolSub.py:102
|
||||
|
|
|
@ -34,7 +34,7 @@ class TclCommandJoinExcellon(TclCommand):
|
|||
help = {
|
||||
'main': "Runs a merge operation (join) on the Excellon objects.\n"
|
||||
"The names of the Excellon objects to be merged will be entered after the outname,\n"
|
||||
"separated by spaces. See the example bellow.\n"
|
||||
"separated by spaces. See the example below.\n"
|
||||
"WARNING: if the name of an Excellon objects has spaces, enclose the name with quotes.",
|
||||
'args': collections.OrderedDict([
|
||||
('outname', 'Name of the new Excellon Object made by joining of other Excellon objects. Required'),
|
||||
|
|
|
@ -34,7 +34,7 @@ class TclCommandJoinGeometry(TclCommand):
|
|||
help = {
|
||||
'main': "Runs a merge operation (join) on the Geometry objects.\n"
|
||||
"The names of the Geometry objects to be merged will be entered after the outname,\n"
|
||||
"separated by spaces. See the example bellow.\n"
|
||||
"separated by spaces. See the example below.\n"
|
||||
"WARNING: if the name of an Geometry objects has spaces, enclose the name with quotes.",
|
||||
'args': collections.OrderedDict([
|
||||
('outname', 'Name of the new Geometry Object made by joining of other Geometry objects. Required'),
|
||||
|
|