Merge branch 'Beta' of https://bitbucket.org/jpcgt/flatcam into extract-prefs-code-from-app-class

This commit is contained in:
David Robertson 2020-04-29 02:57:17 +01:00
commit db37714025
657 changed files with 750 additions and 261 deletions

View File

@ -7,6 +7,29 @@ CHANGELOG for FlatCAM beta
================================================= =================================================
28.04.2020
- handled a possible situation in App.load_defaults() method
- fixed some issues in FlatCAMDB that may appear in certain scenarios
- some minor changes in the Python version detection
- added a new Tcl Command named SetPath which will set a path to be used by the Tcl commands. Once set will serve as a fallback path in case that the files fail to be opened first time. It will be persistent, saved in preferences.
- added the GUI for the new Open Example in the FIle -> Scripting menu.
- I am modifying all the open ... handlers to add a parameter that will flag if the method was launched from Tcl Shell. This way if the method will fail to open the filename (which include the path) it will try to open from a set fallback path.
- fixed issue #406, bug introduced recently (leftover changes).
- modified the ImportSVG Tcl command name to OpenSVG (open_svg alias)
- added a new Tcl command named OpenDXF (open_dxf alias)
- fixed some errors in Scripting features
- added a new Tcl command named GetPath as a convenient way to get the current default path stored in App.defaults['global_tcl_path']
- added a new package to be installed in Linux to make available the black theme for FlatCAM beta
- moved all the 'share' resources (icons) to the 'assets/resources' folder
- some more fixes to problems generated by latest changes in the open handlers
- modified the make_freezed.py script for the new location of the icons
- added a fix for the ConnectionRefusedError in Linux that is issued when first running after a FlatCAM crash
- in SVG parser modified some imports to be one on each line
- fixed the Tcl Command BBox (leftovers from recent global changes)
- fixed some typos in strings reported by @pcb-hobbyst on FlatCAM forum
- disabled a skip_quotes method in ToolShell.FCShell class so I can now use quotes to enclose file paths with spaces inside
27.04.2020 27.04.2020
- finished the moving of all Tcl Shell stuff out of the FlatCAAMApp class to flatcamTools.ToolShell class - finished the moving of all Tcl Shell stuff out of the FlatCAAMApp class to flatcamTools.ToolShell class
@ -24,6 +47,7 @@ CHANGELOG for FlatCAM beta
- added support for Hungarian language - no translation for now - added support for Hungarian language - no translation for now
- minor changes - minor changes
- moved the ObjectCollection class to the flatcamObjects folder where it belongs - moved the ObjectCollection class to the flatcamObjects folder where it belongs
- Linux Makefile
25.04.2020 25.04.2020

View File

@ -14,6 +14,8 @@ if sys.platform == "win32":
# cx_freeze 'module win32' workaround # cx_freeze 'module win32' workaround
pass pass
MIN_VERSION_MAJOR = 3
MIN_VERSION_MINOR = 5
def debug_trace(): def debug_trace():
""" """
@ -32,17 +34,19 @@ if __name__ == '__main__':
# NOTE: Never talk to the GUI from threads! This is why I commented the above. # NOTE: Never talk to the GUI from threads! This is why I commented the above.
freeze_support() freeze_support()
major_v = sys.version_info.major
minor_v = sys.version_info.minor
# Supported Python version is >= 3.5 # Supported Python version is >= 3.5
if sys.version_info.major >= 3: if major_v >= MIN_VERSION_MAJOR:
if sys.version_info.minor >= 5: if minor_v >= MIN_VERSION_MINOR:
pass pass
else: else:
print("FlatCAM BETA uses PYTHON 3. The version minimum is 3.5\n" print("FlatCAM BETA uses PYTHON 3 or later. The version minimum is %s.%s\n"
"Your Python version is: %s" % str(sys.version_info)) "Your Python version is: %s.%s" % (MIN_VERSION_MAJOR, MIN_VERSION_MINOR, str(major_v), str(minor_v)))
os._exit(0) os._exit(0)
else: else:
print("FlatCAM BETA uses PYTHON 3. The version minimum is 3.5\n" print("FlatCAM BETA uses PYTHON 3 or later. The version minimum is %s.%s\n"
"Your Python version is: %s" % str(sys.version_info)) "Your Python version is: %s.%s" % (MIN_VERSION_MAJOR, MIN_VERSION_MINOR, str(major_v), str(minor_v)))
os._exit(0) os._exit(0)
debug_trace() debug_trace()

View File

@ -442,6 +442,12 @@ class App(QtCore.QObject):
self.decimals = int(self.defaults['decimals_metric']) self.decimals = int(self.defaults['decimals_metric'])
else: else:
self.decimals = int(self.defaults['decimals_inch']) self.decimals = int(self.defaults['decimals_inch'])
if self.defaults["global_gray_icons"] is False:
self.resource_location = 'assets/resources'
else:
self.resource_location = 'assets/resources/dark_resources'
self.current_units = self.defaults['units'] self.current_units = self.defaults['units']
@ -804,6 +810,7 @@ class App(QtCore.QObject):
self.ui.menufilenewscript.triggered.connect(self.on_filenewscript) self.ui.menufilenewscript.triggered.connect(self.on_filenewscript)
self.ui.menufileopenscript.triggered.connect(self.on_fileopenscript) self.ui.menufileopenscript.triggered.connect(self.on_fileopenscript)
self.ui.menufileopenscriptexample.triggered.connect(self.on_fileopenscript_example)
self.ui.menufilerunscript.triggered.connect(self.on_filerunscript) self.ui.menufilerunscript.triggered.connect(self.on_filerunscript)
@ -1110,15 +1117,16 @@ class App(QtCore.QObject):
'del', 'drillcncjob', 'export_dxf', 'edxf', 'export_excellon', 'del', 'drillcncjob', 'export_dxf', 'edxf', 'export_excellon',
'export_exc', 'export_exc',
'export_gcode', 'export_gerber', 'export_svg', 'ext', 'exteriors', 'follow', 'export_gcode', 'export_gerber', 'export_svg', 'ext', 'exteriors', 'follow',
'geo_union', 'geocutout', 'get_bounds', 'get_names', 'get_sys', 'help', 'import_svg', 'geo_union', 'geocutout', 'get_bounds', 'get_names', 'get_path', 'get_sys', 'help',
'interiors', 'isolate', 'join_excellon', 'interiors', 'isolate', 'join_excellon',
'join_geometry', 'list_sys', 'milld', 'mills', 'milldrills', 'millslots', 'join_geometry', 'list_sys', 'milld', 'mills', 'milldrills', 'millslots',
'mirror', 'ncc', 'mirror', 'ncc',
'ncr', 'new', 'new_geometry', 'non_copper_regions', 'offset', 'ncr', 'new', 'new_geometry', 'non_copper_regions', 'offset',
'open_excellon', 'open_gcode', 'open_gerber', 'open_project', 'options', 'origin', 'open_dxf', 'open_excellon', 'open_gcode', 'open_gerber', 'open_project', 'open_svg',
'options', 'origin',
'paint', 'panelize', 'plot_all', 'plot_objects', 'plot_status', 'quit_flatcam', 'paint', 'panelize', 'plot_all', 'plot_objects', 'plot_status', 'quit_flatcam',
'save', 'save_project', 'save', 'save_project',
'save_sys', 'scale', 'set_active', 'set_origin', 'set_sys', 'save_sys', 'scale', 'set_active', 'set_origin', 'set_path', 'set_sys',
'skew', 'subtract_poly', 'subtract_rectangle', 'skew', 'subtract_poly', 'subtract_rectangle',
'version', 'write_gcode' 'version', 'write_gcode'
] ]
@ -1815,7 +1823,7 @@ class App(QtCore.QObject):
if silent is False: if silent is False:
self.inform.emit(_("Open Excellon file failed.")) self.inform.emit(_("Open Excellon file failed."))
else: else:
self.on_fileopenexcellon(name=file_name) self.on_fileopenexcellon(name=file_name, signal=None)
return return
gco_list = self.ui.util_defaults_form.fa_gcode_group.gco_list_text.get_value().split(',') gco_list = self.ui.util_defaults_form.fa_gcode_group.gco_list_text.get_value().split(',')
@ -1828,7 +1836,7 @@ class App(QtCore.QObject):
if silent is False: if silent is False:
self.inform.emit(_("Open GCode file failed.")) self.inform.emit(_("Open GCode file failed."))
else: else:
self.on_fileopengcode(name=file_name) self.on_fileopengcode(name=file_name, signal=None)
return return
grb_list = self.ui.util_defaults_form.fa_gerber_group.grb_list_text.get_value().split(',') grb_list = self.ui.util_defaults_form.fa_gerber_group.grb_list_text.get_value().split(',')
@ -1841,7 +1849,7 @@ class App(QtCore.QObject):
if silent is False: if silent is False:
self.inform.emit(_("Open Gerber file failed.")) self.inform.emit(_("Open Gerber file failed."))
else: else:
self.on_fileopengerber(name=file_name) self.on_fileopengerber(name=file_name, signal=None)
return return
# if it reached here without already returning then the app was registered with a file that it does not # if it reached here without already returning then the app was registered with a file that it does not
@ -2945,23 +2953,24 @@ class App(QtCore.QObject):
if text is not None: if text is not None:
new_source_file = text new_source_file = text
else: else:
commands_list = "# AddCircle, AddPolygon, AddPolyline, AddRectangle, AlignDrill, " \ # commands_list = "# AddCircle, AddPolygon, AddPolyline, AddRectangle, AlignDrill, " \
"AlignDrillGrid, Bbox, Bounds, ClearShell, CopperClear,\n" \ # "AlignDrillGrid, Bbox, Bounds, ClearShell, CopperClear,\n" \
"# Cncjob, Cutout, Delete, Drillcncjob, ExportDXF, ExportExcellon, ExportGcode,\n" \ # "# Cncjob, Cutout, Delete, Drillcncjob, ExportDXF, ExportExcellon, ExportGcode,\n" \
"# ExportGerber, ExportSVG, Exteriors, Follow, GeoCutout, GeoUnion, GetNames,\n" \ # "# ExportGerber, ExportSVG, Exteriors, Follow, GeoCutout, GeoUnion, GetNames,\n" \
"# GetSys, ImportSvg, Interiors, Isolate, JoinExcellon, JoinGeometry, " \ # "# GetSys, ImportSvg, Interiors, Isolate, JoinExcellon, JoinGeometry, " \
"ListSys, MillDrills,\n" \ # "ListSys, MillDrills,\n" \
"# MillSlots, Mirror, New, NewExcellon, NewGeometry, NewGerber, Nregions, " \ # "# MillSlots, Mirror, New, NewExcellon, NewGeometry, NewGerber, Nregions, " \
"Offset, OpenExcellon, OpenGCode, OpenGerber, OpenProject,\n" \ # "Offset, OpenExcellon, OpenGCode, OpenGerber, OpenProject,\n" \
"# Options, Paint, Panelize, PlotAl, PlotObjects, SaveProject, " \ # "# Options, Paint, Panelize, PlotAl, PlotObjects, SaveProject, " \
"SaveSys, Scale, SetActive, SetSys, SetOrigin, Skew, SubtractPoly,\n" \ # "SaveSys, Scale, SetActive, SetSys, SetOrigin, Skew, SubtractPoly,\n" \
"# SubtractRectangle, Version, WriteGCode\n" # "# SubtractRectangle, Version, WriteGCode\n"
new_source_file = '# %s\n' % _('CREATE A NEW FLATCAM TCL SCRIPT') + \ new_source_file = '# %s\n' % _('CREATE A NEW FLATCAM TCL SCRIPT') + \
'# %s:\n' % _('TCL Tutorial is here') + \ '# %s:\n' % _('TCL Tutorial is here') + \
'# https://www.tcl.tk/man/tcl8.5/tutorial/tcltutorial.html\n' + '\n\n' + \ '# https://www.tcl.tk/man/tcl8.5/tutorial/tcltutorial.html\n' + '\n\n' + \
'# %s:\n' % _("FlatCAM commands list") '# %s:\n' % _("FlatCAM commands list")
new_source_file += commands_list + '\n' 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): def initialize(obj, app):
obj.source_file = deepcopy(new_source_file) obj.source_file = deepcopy(new_source_file)
@ -3281,11 +3290,11 @@ class App(QtCore.QObject):
self.prog_grid_lay.addWidget(QtWidgets.QLabel('<b>%s</b>' % _("Programmer")), 0, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel('<b>%s</b>' % _("Programmer")), 0, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('<b>%s</b>' % _("Status")), 0, 1) self.prog_grid_lay.addWidget(QtWidgets.QLabel('<b>%s</b>' % _("Status")), 0, 1)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('<b>%s</b>' % _("E-mail")), 0, 2) self.prog_grid_lay.addWidget(QtWidgets.QLabel('<b>%s</b>' % _("E-mail")), 0, 2)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Juan Pablo Caram"), 1, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Juan Pablo Caram"), 1, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Program Author"), 1, 1) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Program Author"), 1, 1)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "<>"), 1, 2) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "<>"), 1, 2)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Denis Hayrullin"), 2, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Denis Hayrullin"), 2, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Kamil Sopko"), 3, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Kamil Sopko"), 3, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu"), 4, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu"), 4, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % _("BETA Maintainer >= 2019")), 4, 1) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % _("BETA Maintainer >= 2019")), 4, 1)
@ -3298,36 +3307,37 @@ class App(QtCore.QObject):
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Victor Benso"), 9, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Victor Benso"), 9, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel(''), 10, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel(''), 10, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Barnaby Walters"), 11, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Jørn Sandvik Nilsson"), 12, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Jørn Sandvik Nilsson"), 12, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Lei Zheng"), 13, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Lei Zheng"), 13, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marco A Quezada"), 14, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Leandro Heck"), 14, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel(''), 12, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marco A Quezada"), 15, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel(''), 16, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Cedric Dussud"), 15, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Cedric Dussud"), 20, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Chris Hemingway"), 16, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Chris Hemingway"), 22, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Damian Wrobel"), 17, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Damian Wrobel"), 24, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Daniel Sallin"), 18, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Daniel Sallin"), 28, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel(''), 19, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel(''), 32, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Bruno Vunderl"), 20, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Bruno Vunderl"), 40, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Gonzalo Lopez"), 21, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Gonzalo Lopez"), 42, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Jakob Staudt"), 22, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Jakob Staudt"), 45, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Mike Smith"), 23, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Mike Smith"), 49, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel(''), 24, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel(''), 52, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Lubos Medovarsky"), 25, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Barnaby Walters"), 55, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Steve Martina"), 26, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Steve Martina"), 57, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Thomas Duffin"), 27, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Thomas Duffin"), 59, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Andrey Kultyapov"), 28, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Andrey Kultyapov"), 61, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel(''), 29, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel(''), 63, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Chris Breneman"), 30, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Chris Breneman"), 65, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Eric Varsanyi"), 31, 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)
self.prog_grid_lay.addWidget(QtWidgets.QLabel(''), 34, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel(''), 74, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "@Idechix"), 100, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "@Idechix"), 100, 0)
self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "@SM"), 101, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "@SM"), 101, 0)
@ -7341,7 +7351,7 @@ class App(QtCore.QObject):
pass pass
# tcl needs to be reinitialized, otherwise old shell variables etc remains # tcl needs to be reinitialized, otherwise old shell variables etc remains
self.init_tcl() self.shell.init_tcl()
self.delete_selection_shape() self.delete_selection_shape()
self.collection.delete_all() self.collection.delete_all()
@ -8335,6 +8345,45 @@ class App(QtCore.QObject):
if filename != '': if filename != '':
self.worker_task.emit({'fcn': self.open_script, 'params': [filename]}) self.worker_task.emit({'fcn': self.open_script, 'params': [filename]})
def on_fileopenscript_example(self, name=None, silent=False):
"""
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:
"""
self.report_usage("on_fileopenscript_example")
App.log.debug("on_fileopenscript_example()")
_filter_ = "TCL script .FlatScript (*.FlatScript);;TCL script .tcl (*.TCL);;TCL script .txt (*.TXT);;" \
"All Files (*.*)"
# test if the app was frozen and choose the path for the configuration file
if getattr(sys, "frozen", False) is True:
example_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + '\\assets\\examples'
else:
example_path = os.path.dirname(os.path.realpath(__file__)) + '\\assets\\examples'
if name:
filenames = [name]
else:
try:
filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(
caption=_("Open TCL script"), directory=example_path, filter=_filter_)
except TypeError:
filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open TCL script"), filter=_filter_)
if len(filenames) == 0:
if silent is False:
self.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled."))
else:
for filename in filenames:
if filename != '':
self.worker_task.emit({'fcn': self.open_script, 'params': [filename]})
def on_filerunscript(self, name=None, silent=False): def on_filerunscript(self, name=None, silent=False):
""" """
File menu callback for loading and running a TCL script. File menu callback for loading and running a TCL script.
@ -9140,7 +9189,7 @@ class App(QtCore.QObject):
self.inform.emit('[WARNING_NOTCL] %s' % _('Could not export DXF file.')) self.inform.emit('[WARNING_NOTCL] %s' % _('Could not export DXF file.'))
return return
def import_svg(self, filename, geo_type='geometry', outname=None): def import_svg(self, filename, geo_type='geometry', outname=None, plot=True):
""" """
Adds a new Geometry Object to the projects and populates Adds a new Geometry Object to the projects and populates
it with shapes extracted from the SVG file. it with shapes extracted from the SVG file.
@ -9175,7 +9224,11 @@ class App(QtCore.QObject):
# Object name # Object name
name = outname or filename.split('/')[-1].split('\\')[-1] name = outname or filename.split('/')[-1].split('\\')[-1]
self.new_object(obj_type, name, obj_init, autoselected=False) ret = self.new_object(obj_type, name, obj_init, autoselected=False, plot=plot)
if ret == 'fail':
self.inform.emit('[ERROR_NOTCL]%s' % _('Import failed.'))
return 'fail'
# Register recent file # Register recent file
self.file_opened.emit("svg", filename) self.file_opened.emit("svg", filename)
@ -9183,7 +9236,7 @@ class App(QtCore.QObject):
# GUI feedback # GUI feedback
self.inform.emit('[success] %s: %s' % (_("Opened"), filename)) self.inform.emit('[success] %s: %s' % (_("Opened"), filename))
def import_dxf(self, filename, geo_type='geometry', outname=None): def import_dxf(self, filename, geo_type='geometry', outname=None, plot=True):
""" """
Adds a new Geometry Object to the projects and populates Adds a new Geometry Object to the projects and populates
it with shapes extracted from the DXF file. it with shapes extracted from the DXF file.
@ -9211,27 +9264,34 @@ class App(QtCore.QObject):
geo_obj.import_dxf(filename, obj_type, units=units) geo_obj.import_dxf(filename, obj_type, units=units)
geo_obj.multigeo = False geo_obj.multigeo = False
with self.proc_container.new(_("Importing DXF")) as proc: with self.proc_container.new(_("Importing DXF")):
# Object name # Object name
name = outname or filename.split('/')[-1].split('\\')[-1] name = outname or filename.split('/')[-1].split('\\')[-1]
self.new_object(obj_type, name, obj_init, autoselected=False) ret = self.new_object(obj_type, name, obj_init, autoselected=False, plot=plot)
if ret == 'fail':
self.inform.emit('[ERROR_NOTCL]%s' % _('Import failed.'))
return 'fail'
# Register recent file # Register recent file
self.file_opened.emit("dxf", filename) self.file_opened.emit("dxf", filename)
# GUI feedback # GUI feedback
self.inform.emit('[success] %s: %s' % (_("Opened"), filename)) self.inform.emit('[success] %s: %s' % (_("Opened"), filename))
def open_gerber(self, filename, outname=None): def open_gerber(self, filename, outname=None, plot=True, from_tcl=False):
""" """
Opens a Gerber file, parses it and creates a new object for Opens a Gerber file, parses it and creates a new object for
it in the program. Thread-safe. it in the program. Thread-safe.
:param outname: Name of the resulting object. None causes the :param outname: Name of the resulting object. None causes the
name to be that of the file. Str. name to be that of the file. Str.
:param filename: Gerber file filename :param filename: Gerber file filename
:type filename: str :type filename: str
:param plot: boolean, to plot or not the resulting object
:param from_tcl: True if run from Tcl Shell
:return: None :return: None
""" """
@ -9265,15 +9325,19 @@ class App(QtCore.QObject):
App.log.debug("open_gerber()") App.log.debug("open_gerber()")
with self.proc_container.new(_("Opening Gerber")) as proc: with self.proc_container.new(_("Opening Gerber")):
# Object name # Object name
name = outname or filename.split('/')[-1].split('\\')[-1] name = outname or filename.split('/')[-1].split('\\')[-1]
# # ## Object creation # ## # # ## Object creation # ##
ret = self.new_object("gerber", name, obj_init, autoselected=False) ret_val = self.new_object("gerber", name, obj_init, autoselected=False, plot=plot)
if ret == 'fail': if ret_val == 'fail':
self.inform.emit('[ERROR_NOTCL]%s' % _(' Open Gerber failed. Probable not a Gerber file.')) if from_tcl:
return 'fail' filename = self.defaults['global_tcl_path'] + '/' + name
ret_val = self.new_object("gerber", name, obj_init, autoselected=False, plot=plot)
if ret_val == 'fail':
self.inform.emit('[ERROR_NOTCL]%s' % _('Open Gerber failed. Probable not a Gerber file.'))
return 'fail'
# Register recent file # Register recent file
self.file_opened.emit("gerber", filename) self.file_opened.emit("gerber", filename)
@ -9281,7 +9345,7 @@ class App(QtCore.QObject):
# GUI feedback # GUI feedback
self.inform.emit('[success] %s: %s' % (_("Opened"), filename)) self.inform.emit('[success] %s: %s' % (_("Opened"), filename))
def open_excellon(self, filename, outname=None, plot=True): def open_excellon(self, filename, outname=None, plot=True, from_tcl=False):
""" """
Opens an Excellon file, parses it and creates a new object for Opens an Excellon file, parses it and creates a new object for
it in the program. Thread-safe. it in the program. Thread-safe.
@ -9290,6 +9354,7 @@ class App(QtCore.QObject):
:param filename: Excellon file filename :param filename: Excellon file filename
:type filename: str :type filename: str
:param plot: boolean, to plot or not the resulting object :param plot: boolean, to plot or not the resulting object
:param from_tcl: True if run from Tcl Shell
:return: None :return: None
""" """
@ -9325,28 +9390,29 @@ class App(QtCore.QObject):
for tool in excellon_obj.tools: for tool in excellon_obj.tools:
if excellon_obj.tools[tool]['solid_geometry']: if excellon_obj.tools[tool]['solid_geometry']:
return return
app_obj.inform.emit('[ERROR_NOTCL] %s: %s' % app_obj.inform.emit('[ERROR_NOTCL] %s: %s' % (_("No geometry found in file"), filename))
(_("No geometry found in file"), filename))
return "fail" return "fail"
with self.proc_container.new(_("Opening Excellon.")): with self.proc_container.new(_("Opening Excellon.")):
# Object name # Object name
name = outname or filename.split('/')[-1].split('\\')[-1] name = outname or filename.split('/')[-1].split('\\')[-1]
ret_val = self.new_object("excellon", name, obj_init, autoselected=False, plot=plot) ret_val = self.new_object("excellon", name, obj_init, autoselected=False, plot=plot)
if ret_val == 'fail': if ret_val == 'fail':
self.inform.emit('[ERROR_NOTCL] %s' % if from_tcl:
_('Open Excellon file failed. Probable not an Excellon file.')) filename = self.defaults['global_tcl_path'] + '/' + name
return ret_val = self.new_object("excellon", name, obj_init, autoselected=False, plot=plot)
if ret_val == 'fail':
self.inform.emit('[ERROR_NOTCL] %s' %
_('Open Excellon file failed. Probable not an Excellon file.'))
return
# Register recent file # Register recent file
self.file_opened.emit("excellon", filename) self.file_opened.emit("excellon", filename)
# GUI feedback # GUI feedback
self.inform.emit('[success] %s: %s' % self.inform.emit('[success] %s: %s' % (_("Opened"), filename))
(_("Opened"), filename))
def open_gcode(self, filename, outname=None, force_parsing=None, plot=True): def open_gcode(self, filename, outname=None, force_parsing=None, plot=True, from_tcl=False):
""" """
Opens a G-gcode file, parses it and creates a new object for Opens a G-gcode file, parses it and creates a new object for
it in the program. Thread-safe. it in the program. Thread-safe.
@ -9354,7 +9420,8 @@ class App(QtCore.QObject):
:param filename: G-code file filename :param filename: G-code file filename
:param outname: Name of the resulting object. None causes the name to be that of the file. :param outname: Name of the resulting object. None causes the name to be that of the file.
:param force_parsing: :param force_parsing:
:param plot: :param plot: If True plot the object on canvas
:param from_tcl: True if run from Tcl Shell
:return: None :return: None
""" """
App.log.debug("open_gcode()") App.log.debug("open_gcode()")
@ -9374,16 +9441,14 @@ class App(QtCore.QObject):
gcode = f.read() gcode = f.read()
f.close() f.close()
except IOError: except IOError:
app_obj_.inform.emit('[ERROR_NOTCL] %s: %s' % app_obj_.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Failed to open"), filename))
(_("Failed to open"), filename))
return "fail" return "fail"
job_obj.gcode = gcode job_obj.gcode = gcode
gcode_ret = job_obj.gcode_parse(force_parsing=force_parsing) gcode_ret = job_obj.gcode_parse(force_parsing=force_parsing)
if gcode_ret == "fail": if gcode_ret == "fail":
self.inform.emit('[ERROR_NOTCL] %s' % self.inform.emit('[ERROR_NOTCL] %s' % _("This is not GCODE"))
_("This is not GCODE"))
return "fail" return "fail"
job_obj.create_geometry() job_obj.create_geometry()
@ -9394,21 +9459,24 @@ class App(QtCore.QObject):
name = outname or filename.split('/')[-1].split('\\')[-1] name = outname or filename.split('/')[-1].split('\\')[-1]
# New object creation and file processing # New object creation and file processing
obj_ret = self.new_object("cncjob", name, obj_init, autoselected=False, plot=plot) ret_val = self.new_object("cncjob", name, obj_init, autoselected=False, plot=plot)
if obj_ret == 'fail': if ret_val == 'fail':
self.inform.emit('[ERROR_NOTCL] %s' % if from_tcl:
_("Failed to create CNCJob Object. Probable not a GCode file. " filename = self.defaults['global_tcl_path'] + '/' + name
"Try to load it from File menu.\n " ret_val = self.new_object("cncjob", name, obj_init, autoselected=False, plot=plot)
"Attempting to create a FlatCAM CNCJob Object from " if ret_val == 'fail':
"G-Code file failed during processing")) self.inform.emit('[ERROR_NOTCL] %s' %
return "fail" _("Failed to create CNCJob Object. Probable not a GCode file. "
"Try to load it from File menu.\n "
"Attempting to create a FlatCAM CNCJob Object from "
"G-Code file failed during processing"))
return "fail"
# Register recent file # Register recent file
self.file_opened.emit("cncjob", filename) self.file_opened.emit("cncjob", filename)
# GUI feedback # GUI feedback
self.inform.emit('[success] %s: %s' % self.inform.emit('[success] %s: %s' % (_("Opened"), filename))
(_("Opened"), filename))
def open_hpgl2(self, filename, outname=None): def open_hpgl2(self, filename, outname=None):
""" """
@ -9556,7 +9624,7 @@ class App(QtCore.QObject):
(_("Failed to open config file"), filename)) (_("Failed to open config file"), filename))
return return
def open_project(self, filename, run_from_arg=None, plot=True, cli=None): def open_project(self, filename, run_from_arg=None, plot=True, cli=None, from_tcl=False):
""" """
Loads a project from the specified file. Loads a project from the specified file.
@ -9571,6 +9639,7 @@ class App(QtCore.QObject):
:param run_from_arg: True if run for arguments :param run_from_arg: True if run for arguments
:param plot: If True plot all objects in the project :param plot: If True plot all objects in the project
:param cli: Run from command line :param cli: Run from command line
:param from_tcl: True if run from Tcl Sehll
:return: None :return: None
""" """
App.log.debug("Opening project: " + filename) App.log.debug("Opening project: " + filename)
@ -9594,16 +9663,25 @@ class App(QtCore.QObject):
try: try:
f = open(filename, 'r') f = open(filename, 'r')
except IOError: except IOError:
App.log.error("Failed to open project file: %s" % filename) if from_tcl:
self.inform.emit('[ERROR_NOTCL] %s: %s' % name = filename.split('/')[-1].split('\\')[-1]
(_("Failed to open project file"), filename)) filename = self.defaults['global_tcl_path'] + '/' + name
return try:
f = open(filename, 'r')
except IOError:
log.error("Failed to open project file: %s" % filename)
self.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Failed to open project file"), filename))
return
else:
log.error("Failed to open project file: %s" % filename)
self.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Failed to open project file"), filename))
return
try: try:
d = json.load(f, object_hook=dict2obj) d = json.load(f, object_hook=dict2obj)
except Exception as e: except Exception as e:
App.log.error("Failed to parse project file, trying to see if it loads as an LZMA archive: %s because %s" % log.error("Failed to parse project file, trying to see if it loads as an LZMA archive: %s because %s" %
(filename, str(e))) (filename, str(e)))
f.close() f.close()
# Open and parse a compressed Project file # Open and parse a compressed Project file
@ -10516,7 +10594,7 @@ class App(QtCore.QObject):
for obj in objects: for obj in objects:
obj.on_generatecnc_button_click() obj.on_generatecnc_button_click()
def save_project(self, filename, quit_action=False, silent=False): def save_project(self, filename, quit_action=False, silent=False, from_tcl=False):
""" """
Saves the current project to the specified file. Saves the current project to the specified file.
@ -10524,6 +10602,7 @@ class App(QtCore.QObject):
:type filename: str :type filename: str
:param quit_action: if the project saving will be followed by an app quit; boolean :param quit_action: if the project saving will be followed by an app quit; boolean
:param silent: if True will not display status messages :param silent: if True will not display status messages
:param from_tcl True is run from Tcl Shell
:return: None :return: None
""" """
self.log.debug("save_project()") self.log.debug("save_project()")
@ -10781,16 +10860,26 @@ class ArgsThread(QtCore.QObject):
conn = self.listener.accept() conn = self.listener.accept()
self.serve(conn) self.serve(conn)
except socket.error: except socket.error:
conn = Client(*address) try:
conn.send(sys.argv) conn = Client(*address)
conn.send('close') conn.send(sys.argv)
# close the current instance only if there are args conn.send('close')
if len(sys.argv) > 1: # close the current instance only if there are args
try: if len(sys.argv) > 1:
self.listener.close() try:
except Exception: self.listener.close()
except Exception:
pass
sys.exit()
except ConnectionRefusedError:
if sys.platform == 'win32':
pass pass
sys.exit() else:
os.system('rm /tmp/testipc')
self.listener = Listener(*address)
while True:
conn = self.listener.accept()
self.serve(conn)
def serve(self, conn): def serve(self, conn):
while True: while True:

View File

@ -916,19 +916,19 @@ class ToolsDB(QtWidgets.QWidget):
self.app.ui.plot_tab_area.removeTab(idx) self.app.ui.plot_tab_area.removeTab(idx)
self.app.inform.emit('%s' % _("Cancelled adding tool from DB.")) self.app.inform.emit('%s' % _("Cancelled adding tool from DB."))
def resize_new_tool_table_widget(self, min_size, max_size): # def resize_new_tool_table_widget(self, min_size, max_size):
""" # """
Resize the table widget responsible for adding new tool in the Tool Database # Resize the table widget responsible for adding new tool in the Tool Database
#
:param min_size: passed by rangeChanged signal or the self.new_tool_table_widget.horizontalScrollBar() # :param min_size: passed by rangeChanged signal or the self.new_tool_table_widget.horizontalScrollBar()
:param max_size: passed by rangeChanged signal or the self.new_tool_table_widget.horizontalScrollBar() # :param max_size: passed by rangeChanged signal or the self.new_tool_table_widget.horizontalScrollBar()
:return: # :return:
""" # """
t_height = self.t_height # t_height = self.t_height
if max_size > min_size: # if max_size > min_size:
t_height = self.t_height + self.new_tool_table_widget.verticalScrollBar().height() # t_height = self.t_height + self.new_tool_table_widget.verticalScrollBar().height()
#
self.new_tool_table_widget.setMaximumHeight(t_height) # self.new_tool_table_widget.setMaximumHeight(t_height)
def closeEvent(self, QCloseEvent): def closeEvent(self, QCloseEvent):
super().closeEvent(QCloseEvent) super().closeEvent(QCloseEvent)
@ -2120,7 +2120,7 @@ class ToolsDB2(QtWidgets.QWidget):
json.dump(self.db_tool_dict, f, default=to_dict, indent=2) json.dump(self.db_tool_dict, f, default=to_dict, indent=2)
except Exception as e: except Exception as e:
self.app.log.debug("App.on_save_tools_db() --> %s" % str(e)) self.app.log.debug("App.on_save_tools_db() --> %s" % str(e))
self.inform.emit('[ERROR_NOTCL] %s' % _("Failed to write Tools DB to file.")) self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed to write Tools DB to file."))
return return
except Exception: except Exception:
self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed to write Tools DB to file.")) self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed to write Tools DB to file."))

View File

@ -88,9 +88,9 @@ def on_language_apply_click(app, restart=False):
theme = 'white' theme = 'white'
if theme == 'white': if theme == 'white':
resource_loc = 'share' resource_loc = 'assets/resources'
else: else:
resource_loc = 'share' resource_loc = 'assets/resources'
# do nothing if trying to apply the language that is the current language (already applied). # do nothing if trying to apply the language that is the current language (already applied).
settings = QSettings("Open Source", "FlatCAM") settings = QSettings("Open Source", "FlatCAM")
@ -180,9 +180,9 @@ def restart_program(app, ask=None):
theme = 'white' theme = 'white'
if theme == 'white': if theme == 'white':
resource_loc = 'share' resource_loc = 'assets/resources'
else: else:
resource_loc = 'share' resource_loc = 'assets/resources'
# close the Socket in ArgsThread class # close the Socket in ArgsThread class
app.new_launch.listener.close() app.new_launch.listener.close()

View File

@ -14,10 +14,19 @@ ASSEST_PATH = assets/linux
INSTALL_PATH = /usr/share/flatcam-beta INSTALL_PATH = /usr/share/flatcam-beta
APPS_PATH = /usr/share/applications APPS_PATH = /usr/share/applications
MIN_PY3_MINOR_VERSION := 5
PY3_MINOR_VERSION := $(shell python3 --version | cut -d'.' -f2)
ifneq ($(MIN_PY3_MINOR_VERSION), $(firstword $(sort $(PY3_MINOR_VERSION) $(MIN_PY3_MINOR_VERSION))))
$(info Current python version is 3.$(PY3_MINOR_VERSION))
$(error You must have at least 3.$(MIN_PY3_MINOR_VERSION) installed)
endif
install: install:
ifeq ($(USER_ID), 0) ifeq ($(USER_ID), 0)
@ echo "Installing it system-wide" @ echo "Installing it system-wide"
cp -rf $(LOCAL_PATH) $(INSTALL_PATH) cp -rf $(LOCAL_PATH) $(INSTALL_PATH)
@ sed -i "s|python_script_path=.*|python_script_path=$(INSTALL_PATH)|g" $(INSTALL_PATH)/assets/linux/flatcam-beta
ln -sf $(INSTALL_PATH)/assets/linux/flatcam-beta /usr/local/bin ln -sf $(INSTALL_PATH)/assets/linux/flatcam-beta /usr/local/bin
cp -f $(ASSEST_PATH)/flatcam-beta.desktop $(APPS_PATH) cp -f $(ASSEST_PATH)/flatcam-beta.desktop $(APPS_PATH)
@ sed -i "s|Exec=.*|Exec=$(INSTALL_PATH)/$(ASSEST_PATH)/flatcam-beta|g" $(APPS_PATH)/flatcam-beta.desktop @ sed -i "s|Exec=.*|Exec=$(INSTALL_PATH)/$(ASSEST_PATH)/flatcam-beta|g" $(APPS_PATH)/flatcam-beta.desktop

View File

@ -1,18 +1,19 @@
FlatCAM BETA (c) 2019 - by Marius Stanciu FlatCAM BETA (c) 2019 - by Marius Stanciu
Based on FlatCAM: Based on FlatCAM:
2D Computer-Aided PCB Manufacturing by (c) 2014-2016 Juan Pablo Caram 2D Computer-Aided PCB Manufacturing by (c) 2014-2016 Juan Pablo Caram
================================================= =====================================================================
FlatCAM is a program for preparing CNC jobs for making PCBs on a CNC router. FlatCAM is a program for preparing CNC jobs for making PCBs on a CNC router.
Among other things, it can take a Gerber file generated by your favorite PCB Among other things, it can take a Gerber file generated by your favorite PCB
CAD program, and create G-Code for Isolation routing. CAD program, and create G-Code for Isolation routing.
================================================= =====================================================================
------------ Installation instructions ---------- -------------------------- Installation instructions ----------------
Works with Python version 3.5 or greater and PyQt5. Works with Python version 3.5 or greater and PyQt5.
More on the YouTube channel: https://www.youtube.com/playlist?list=PLVvP2SYRpx-AQgNlfoxw93tXUXon7G94_ More on the YouTube channel: https://www.youtube.com/playlist?list=PLVvP2SYRpx-AQgNlfoxw93tXUXon7G94_
You can contact me on my email address found in the app in: You can contact me on my email address found in the app in:
Menu -> Help -> About FlatCAM -> Programmers -> Marius Stanciu Menu -> Help -> About FlatCAM -> Programmers -> Marius Stanciu
@ -20,7 +21,8 @@ Menu -> Help -> About FlatCAM -> Programmers -> Marius Stanciu
- Download sources from: https://bitbucket.org/jpcgt/flatcam/downloads/ - Download sources from: https://bitbucket.org/jpcgt/flatcam/downloads/
- Unzip them on a HDD location that your user has permissions for. - Unzip them on a HDD location that your user has permissions for.
1. Windows 1.Windows
- download the provided installer (for your OS flavor 64bit or 32bit) from: - download the provided installer (for your OS flavor 64bit or 32bit) from:
https://bitbucket.org/jpcgt/flatcam/downloads/ https://bitbucket.org/jpcgt/flatcam/downloads/
- execute the installer and install the program. It is recommended to install as a Local User. - execute the installer and install the program. It is recommended to install as a Local User.
@ -33,7 +35,10 @@ Use one of the versions (64bit or 32it) that are compatible with your OS.
To save space use one of the versions that have the smaller size (they offer 2 versions: one with size of few hundred MB and one smaller with size of few tens of MB) To save space use one of the versions that have the smaller size (they offer 2 versions: one with size of few hundred MB and one smaller with size of few tens of MB)
- add Python folder and Python\Scripts folder to your Windows Path (https://docs.microsoft.com/en-us/previous-versions/office/developer/sharepoint-2010/ee537574(v%3Doffice.14)) - add Python folder and Python\Scripts folder to your Windows Path (https://docs.microsoft.com/en-us/previous-versions/office/developer/sharepoint-2010/ee537574(v%3Doffice.14))
- verify that the pip package can be run by opening Command Prompt(Admin) and running the command: pip -V - verify that the pip package can be run by opening Command Prompt(Admin) and running the command:
```
pip -V
```
- look in the requirements.txt file (found in the sources folder) and install all the dependencies using the pip package. - look in the requirements.txt file (found in the sources folder) and install all the dependencies using the pip package.
The required wheels can be downloaded either from: The required wheels can be downloaded either from:
@ -42,23 +47,41 @@ or
https://pypi.org/ https://pypi.org/
You can download all the required wheels files into a folder (e.g D:\my_folder) and install them from Command Prompt like this: You can download all the required wheels files into a folder (e.g D:\my_folder) and install them from Command Prompt like this:
```
cd D:\my_folder cd D:\my_folder
```
and for each wheel file (*.whl) run: and for each wheel file (*.whl) run:
```
D:\my_folder\> pip install --upgrade package_from_requirements.whl D:\my_folder\> pip install --upgrade package_from_requirements.whl
```
Run FlatCAM beta from the installation folder (e.g D:\FlatCAM_beta) in the Command Prompt with the following command: Run FlatCAM beta from the installation folder (e.g D:\FlatCAM_beta) in the Command Prompt with the following command:
cd D:\FlatCAM_beta cd D:\FlatCAM_beta
python FlatCAM.py python FlatCAM.py
2. Linux 2.Linux
- make sure that Python 3.8 is installed on your OS and that the command: python3 -V confirm it - make sure that Python 3.8 is installed on your OS and that the command: python3 -V confirm it
- verify that the pip package is installed for your Python installation (e.g 3.8) by running the command pip3 -V. - verify that the pip package is installed for your Python installation (e.g 3.8) by running the command:
```
pip3 -V
```
If it is not installed, install it. In Ubuntu-like OS's it is done like this: If it is not installed, install it. In Ubuntu-like OS's it is done like this:
```
sudo apt-get install python3-pip sudo apt-get install python3-pip
```
or: or:
```
sudo apt-get install python3.8-pip sudo apt-get install python3.8-pip
```
- verify that the file setup_ubuntu.sh has Linux line-endings (LF) and that it is executable (chmod +x setup_ubuntu.sh) - verify that the file setup_ubuntu.sh has Linux line-endings (LF) and that it is executable (chmod +x setup_ubuntu.sh)
- run the file setup_ubuntu.sh and install all the dependencies with the command: ./setup_ubuntu.sh - run the file setup_ubuntu.sh and install all the dependencies with the command:
```
./setup_ubuntu.sh
```
- if the previous command is successful and has no errors, run FlatCAM with the command: python3 FlatCAM.py - if the previous command is successful and has no errors, run FlatCAM with the command: python3 FlatCAM.py
- Alternatively you can install it on Ubuntu with: - Alternatively you can install it on Ubuntu with:
@ -73,7 +96,8 @@ make install
sudo make install sudo make install
``` ```
3. MacOS 3.MacOS
Instructions from here: https://gist.github.com/natevw/3e6fc929aff358b38c0a#gistcomment-3111878 Instructions from here: https://gist.github.com/natevw/3e6fc929aff358b38c0a#gistcomment-3111878
- create a folder to hold the sources somewhere on your HDD: - create a folder to hold the sources somewhere on your HDD:

View File

@ -1,10 +1,5 @@
#!/bin/bash #!/bin/bash
current_path=$(dirname $0) script_path=$(readlink -f $0)
cd $current_path python_script_path=$(dirname $script_path)/../../
current_path=$(pwd) python3 $python_script_path/FlatCAM.py $*
cd -
script_path=$(dirname $current_path)
python3 $script_path/FlatCAM.py $*

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

Before

Width:  |  Height:  |  Size: 420 B

After

Width:  |  Height:  |  Size: 420 B

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 509 B

After

Width:  |  Height:  |  Size: 509 B

View File

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 406 B

View File

Before

Width:  |  Height:  |  Size: 400 B

After

Width:  |  Height:  |  Size: 400 B

View File

Before

Width:  |  Height:  |  Size: 361 B

After

Width:  |  Height:  |  Size: 361 B

View File

Before

Width:  |  Height:  |  Size: 521 B

After

Width:  |  Height:  |  Size: 521 B

View File

Before

Width:  |  Height:  |  Size: 403 B

After

Width:  |  Height:  |  Size: 403 B

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 457 B

After

Width:  |  Height:  |  Size: 457 B

View File

Before

Width:  |  Height:  |  Size: 539 B

After

Width:  |  Height:  |  Size: 539 B

View File

Before

Width:  |  Height:  |  Size: 364 B

After

Width:  |  Height:  |  Size: 364 B

View File

Before

Width:  |  Height:  |  Size: 331 B

After

Width:  |  Height:  |  Size: 331 B

View File

Before

Width:  |  Height:  |  Size: 363 B

After

Width:  |  Height:  |  Size: 363 B

View File

Before

Width:  |  Height:  |  Size: 344 B

After

Width:  |  Height:  |  Size: 344 B

View File

Before

Width:  |  Height:  |  Size: 517 B

After

Width:  |  Height:  |  Size: 517 B

View File

Before

Width:  |  Height:  |  Size: 838 B

After

Width:  |  Height:  |  Size: 838 B

View File

Before

Width:  |  Height:  |  Size: 546 B

After

Width:  |  Height:  |  Size: 546 B

View File

Before

Width:  |  Height:  |  Size: 676 B

After

Width:  |  Height:  |  Size: 676 B

View File

Before

Width:  |  Height:  |  Size: 798 B

After

Width:  |  Height:  |  Size: 798 B

View File

Before

Width:  |  Height:  |  Size: 593 B

After

Width:  |  Height:  |  Size: 593 B

View File

Before

Width:  |  Height:  |  Size: 467 B

After

Width:  |  Height:  |  Size: 467 B

View File

Before

Width:  |  Height:  |  Size: 332 B

After

Width:  |  Height:  |  Size: 332 B

View File

Before

Width:  |  Height:  |  Size: 339 B

After

Width:  |  Height:  |  Size: 339 B

View File

Before

Width:  |  Height:  |  Size: 183 B

After

Width:  |  Height:  |  Size: 183 B

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Before

Width:  |  Height:  |  Size: 459 B

After

Width:  |  Height:  |  Size: 459 B

View File

Before

Width:  |  Height:  |  Size: 491 B

After

Width:  |  Height:  |  Size: 491 B

View File

Before

Width:  |  Height:  |  Size: 626 B

After

Width:  |  Height:  |  Size: 626 B

View File

Before

Width:  |  Height:  |  Size: 371 B

After

Width:  |  Height:  |  Size: 371 B

View File

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 406 B

View File

Before

Width:  |  Height:  |  Size: 470 B

After

Width:  |  Height:  |  Size: 470 B

View File

Before

Width:  |  Height:  |  Size: 470 B

After

Width:  |  Height:  |  Size: 470 B

View File

Before

Width:  |  Height:  |  Size: 574 B

After

Width:  |  Height:  |  Size: 574 B

View File

Before

Width:  |  Height:  |  Size: 481 B

After

Width:  |  Height:  |  Size: 481 B

View File

Before

Width:  |  Height:  |  Size: 524 B

After

Width:  |  Height:  |  Size: 524 B

View File

Before

Width:  |  Height:  |  Size: 470 B

After

Width:  |  Height:  |  Size: 470 B

View File

Before

Width:  |  Height:  |  Size: 875 B

After

Width:  |  Height:  |  Size: 875 B

View File

Before

Width:  |  Height:  |  Size: 546 B

After

Width:  |  Height:  |  Size: 546 B

View File

Before

Width:  |  Height:  |  Size: 675 B

After

Width:  |  Height:  |  Size: 675 B

View File

Before

Width:  |  Height:  |  Size: 347 B

After

Width:  |  Height:  |  Size: 347 B

View File

Before

Width:  |  Height:  |  Size: 441 B

After

Width:  |  Height:  |  Size: 441 B

View File

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 544 B

View File

Before

Width:  |  Height:  |  Size: 716 B

After

Width:  |  Height:  |  Size: 716 B

View File

Before

Width:  |  Height:  |  Size: 599 B

After

Width:  |  Height:  |  Size: 599 B

View File

Before

Width:  |  Height:  |  Size: 677 B

After

Width:  |  Height:  |  Size: 677 B

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 485 B

After

Width:  |  Height:  |  Size: 485 B

View File

Before

Width:  |  Height:  |  Size: 648 B

After

Width:  |  Height:  |  Size: 648 B

View File

Before

Width:  |  Height:  |  Size: 552 B

After

Width:  |  Height:  |  Size: 552 B

View File

Before

Width:  |  Height:  |  Size: 786 B

After

Width:  |  Height:  |  Size: 786 B

View File

Before

Width:  |  Height:  |  Size: 626 B

After

Width:  |  Height:  |  Size: 626 B

View File

Before

Width:  |  Height:  |  Size: 383 B

After

Width:  |  Height:  |  Size: 383 B

View File

Before

Width:  |  Height:  |  Size: 565 B

After

Width:  |  Height:  |  Size: 565 B

View File

Before

Width:  |  Height:  |  Size: 569 B

After

Width:  |  Height:  |  Size: 569 B

View File

Before

Width:  |  Height:  |  Size: 958 B

After

Width:  |  Height:  |  Size: 958 B

View File

Before

Width:  |  Height:  |  Size: 569 B

After

Width:  |  Height:  |  Size: 569 B

View File

Before

Width:  |  Height:  |  Size: 534 B

After

Width:  |  Height:  |  Size: 534 B

View File

Before

Width:  |  Height:  |  Size: 669 B

After

Width:  |  Height:  |  Size: 669 B

View File

Before

Width:  |  Height:  |  Size: 453 B

After

Width:  |  Height:  |  Size: 453 B

View File

Before

Width:  |  Height:  |  Size: 590 B

After

Width:  |  Height:  |  Size: 590 B

View File

Before

Width:  |  Height:  |  Size: 582 B

After

Width:  |  Height:  |  Size: 582 B

View File

Before

Width:  |  Height:  |  Size: 838 B

After

Width:  |  Height:  |  Size: 838 B

View File

Before

Width:  |  Height:  |  Size: 481 B

After

Width:  |  Height:  |  Size: 481 B

View File

Before

Width:  |  Height:  |  Size: 570 B

After

Width:  |  Height:  |  Size: 570 B

View File

Before

Width:  |  Height:  |  Size: 654 B

After

Width:  |  Height:  |  Size: 654 B

View File

Before

Width:  |  Height:  |  Size: 1013 B

After

Width:  |  Height:  |  Size: 1013 B

View File

Before

Width:  |  Height:  |  Size: 407 B

After

Width:  |  Height:  |  Size: 407 B

View File

Before

Width:  |  Height:  |  Size: 540 B

After

Width:  |  Height:  |  Size: 540 B

View File

Before

Width:  |  Height:  |  Size: 679 B

After

Width:  |  Height:  |  Size: 679 B

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Some files were not shown because too many files have changed in this diff Show More