jpcgt/flatcam/Beta слито с Beta
This commit is contained in:
commit
f6dedfea49
305
FlatCAMApp.py
305
FlatCAMApp.py
|
@ -23,8 +23,14 @@ from stat import S_IREAD, S_IRGRP, S_IROTH
|
|||
import subprocess
|
||||
import ctypes
|
||||
|
||||
import tkinter as tk
|
||||
from PyQt5 import QtPrintSupport
|
||||
# import tkinter as tk
|
||||
# from PyQt5 import QtPrintSupport
|
||||
|
||||
from reportlab.graphics import renderPDF
|
||||
from reportlab.pdfgen import canvas
|
||||
from reportlab.graphics import renderPM
|
||||
from reportlab.lib.units import inch, mm
|
||||
from reportlab.lib.pagesizes import landscape, portrait
|
||||
|
||||
from contextlib import contextmanager
|
||||
import gc
|
||||
|
@ -57,6 +63,8 @@ from flatcamEditors.FlatCAMExcEditor import FlatCAMExcEditor
|
|||
from flatcamEditors.FlatCAMGrbEditor import FlatCAMGrbEditor
|
||||
from flatcamEditors.FlatCAMTextEditor import TextEditor
|
||||
|
||||
from flatcamParsers.ParseHPGL2 import HPGL2
|
||||
|
||||
from FlatCAMProcess import *
|
||||
from FlatCAMWorkerStack import WorkerStack
|
||||
# from flatcamGUI.VisPyVisuals import Color
|
||||
|
@ -133,7 +141,7 @@ class App(QtCore.QObject):
|
|||
# ################## Version and VERSION DATE ##############################
|
||||
# ##########################################################################
|
||||
version = 8.99
|
||||
version_date = "2019/12/12"
|
||||
version_date = "2019/12/15"
|
||||
beta = True
|
||||
engine = '3D'
|
||||
|
||||
|
@ -591,7 +599,7 @@ class App(QtCore.QObject):
|
|||
"excellon_travelz": 2,
|
||||
"excellon_endz": 0.5,
|
||||
"excellon_feedrate": 300,
|
||||
"excellon_spindlespeed": None,
|
||||
"excellon_spindlespeed": 0,
|
||||
"excellon_dwell": False,
|
||||
"excellon_dwelltime": 1,
|
||||
"excellon_toolchange": False,
|
||||
|
@ -658,7 +666,7 @@ class App(QtCore.QObject):
|
|||
"geometry_endz": 15.0,
|
||||
"geometry_feedrate": 120,
|
||||
"geometry_feedrate_z": 60,
|
||||
"geometry_spindlespeed": None,
|
||||
"geometry_spindlespeed": 0,
|
||||
"geometry_dwell": False,
|
||||
"geometry_dwelltime": 1,
|
||||
"geometry_ppname_g": 'default',
|
||||
|
@ -668,6 +676,7 @@ class App(QtCore.QObject):
|
|||
"geometry_startz": None,
|
||||
"geometry_feedrate_rapid": 1500,
|
||||
"geometry_extracut": False,
|
||||
"geometry_extracut_length": 0.1,
|
||||
"geometry_z_pdepth": -0.02,
|
||||
"geometry_f_plunge": False,
|
||||
"geometry_spindledir": 'CW',
|
||||
|
@ -901,12 +910,14 @@ class App(QtCore.QObject):
|
|||
"tools_cal_verz": 0.1,
|
||||
"tools_cal_zeroz": False,
|
||||
"tools_cal_toolchangez": 15,
|
||||
"tools_cal_toolchange_xy": '',
|
||||
"tools_cal_sec_point": 'tl',
|
||||
|
||||
# Utilities
|
||||
# file associations
|
||||
"fa_excellon": 'drd, drl, exc, ncd, tap, xln',
|
||||
"fa_gcode": 'cnc, din, dnc, ecs, eia, fan, fgc, fnc, gc, gcd, gcode, h, hnc, i, min, mpf, mpr, nc, ncc, '
|
||||
'ncg, ncp, ngc, out, plt, ply, rol, sbp, tap, xpi',
|
||||
'ncg, ncp, ngc, out, ply, rol, sbp, tap, xpi',
|
||||
"fa_gerber": 'art, bot, bsm, cmp, crc, crs, dim, gb0, gb1, gb2, gb3, gb4, gb5, gb6, gb7, gb8, gb9, gbd, '
|
||||
'gbl, gbo, gbp, gbr, gbs, gdo, ger, gko, gm1, gm2, gm3, grb, gtl, gto, gtp, gts, ly15, ly2, '
|
||||
'mil, pho, plc, pls, smb, smt, sol, spb, spt, ssb, sst, stc, sts, top, tsm',
|
||||
|
@ -1260,6 +1271,7 @@ class App(QtCore.QObject):
|
|||
"geometry_startz": self.ui.geometry_defaults_form.geometry_adv_opt_group.gstartz_entry,
|
||||
"geometry_feedrate_rapid": self.ui.geometry_defaults_form.geometry_adv_opt_group.cncfeedrate_rapid_entry,
|
||||
"geometry_extracut": self.ui.geometry_defaults_form.geometry_adv_opt_group.extracut_cb,
|
||||
"geometry_extracut_length": self.ui.geometry_defaults_form.geometry_adv_opt_group.e_cut_entry,
|
||||
"geometry_z_pdepth": self.ui.geometry_defaults_form.geometry_adv_opt_group.pdepth_entry,
|
||||
"geometry_feedrate_probe": self.ui.geometry_defaults_form.geometry_adv_opt_group.feedrate_probe_entry,
|
||||
"geometry_spindledir": self.ui.geometry_defaults_form.geometry_adv_opt_group.spindledir_radio,
|
||||
|
@ -1482,6 +1494,8 @@ class App(QtCore.QObject):
|
|||
"tools_cal_verz": self.ui.tools2_defaults_form.tools2_cal_group.verz_entry,
|
||||
"tools_cal_zeroz": self.ui.tools2_defaults_form.tools2_cal_group.zeroz_cb,
|
||||
"tools_cal_toolchangez": self.ui.tools2_defaults_form.tools2_cal_group.toolchangez_entry,
|
||||
"tools_cal_toolchange_xy": self.ui.tools2_defaults_form.tools2_cal_group.toolchange_xy_entry,
|
||||
"tools_cal_sec_point": self.ui.tools2_defaults_form.tools2_cal_group.second_point_radio,
|
||||
|
||||
# Utilities
|
||||
# File associations
|
||||
|
@ -1759,7 +1773,7 @@ class App(QtCore.QObject):
|
|||
|
||||
self.ui.menufileimportdxf.triggered.connect(lambda: self.on_file_importdxf("geometry"))
|
||||
self.ui.menufileimportdxf_as_gerber.triggered.connect(lambda: self.on_file_importdxf("gerber"))
|
||||
|
||||
self.ui.menufileimport_hpgl2_as_geo.triggered.connect(self.on_fileopenhpgl2)
|
||||
self.ui.menufileexportsvg.triggered.connect(self.on_file_exportsvg)
|
||||
self.ui.menufileexportpng.triggered.connect(self.on_file_exportpng)
|
||||
self.ui.menufileexportexcellon.triggered.connect(self.on_file_exportexcellon)
|
||||
|
@ -1770,6 +1784,7 @@ class App(QtCore.QObject):
|
|||
self.ui.menufilesaveproject.triggered.connect(self.on_file_saveproject)
|
||||
self.ui.menufilesaveprojectas.triggered.connect(self.on_file_saveprojectas)
|
||||
self.ui.menufilesaveprojectcopy.triggered.connect(lambda: self.on_file_saveprojectas(make_copy=True))
|
||||
self.ui.menufilesave_object_pdf.triggered.connect(self.on_file_save_object_pdf)
|
||||
self.ui.menufilesavedefaults.triggered.connect(self.on_file_savedefaults)
|
||||
|
||||
self.ui.menufileexportpref.triggered.connect(self.on_export_preferences)
|
||||
|
@ -2476,6 +2491,7 @@ class App(QtCore.QObject):
|
|||
|
||||
# variable to store coordinates
|
||||
self.pos = (0, 0)
|
||||
self.pos_canvas = (0, 0)
|
||||
self.pos_jump = (0, 0)
|
||||
|
||||
# variable to store mouse coordinates
|
||||
|
@ -2545,7 +2561,7 @@ class App(QtCore.QObject):
|
|||
self.exc_list = ['drd', 'drl', 'exc', 'ncd', 'tap', 'txt', 'xln']
|
||||
|
||||
self.gcode_list = ['cnc', 'din', 'dnc', 'ecs', 'eia', 'fan', 'fgc', 'fnc', 'gc', 'gcd', 'gcode', 'h', 'hnc',
|
||||
'i', 'min', 'mpf', 'mpr', 'nc', 'ncc', 'ncg', 'ngc', 'ncp', 'out', 'plt', 'ply', 'rol',
|
||||
'i', 'min', 'mpf', 'mpr', 'nc', 'ncc', 'ncg', 'ngc', 'ncp', 'out', 'ply', 'rol',
|
||||
'sbp', 'tap', 'xpi']
|
||||
self.svg_list = ['svg']
|
||||
self.dxf_list = ['dxf']
|
||||
|
@ -2979,7 +2995,7 @@ class App(QtCore.QObject):
|
|||
self.dblsidedtool.install(icon=QtGui.QIcon('share/doubleside16.png'), separator=True)
|
||||
|
||||
self.cal_exc_tool = ToolCalibration(self)
|
||||
self.cal_exc_tool.install(icon=QtGui.QIcon('share/drill16.png'), pos=self.ui.menutool,
|
||||
self.cal_exc_tool.install(icon=QtGui.QIcon('share/calibrate_16.png'), pos=self.ui.menutool,
|
||||
before=self.dblsidedtool.menuAction,
|
||||
separator=False)
|
||||
self.distance_tool = Distance(self)
|
||||
|
@ -3148,6 +3164,7 @@ class App(QtCore.QObject):
|
|||
|
||||
# Tools Toolbar Signals
|
||||
self.ui.dblsided_btn.triggered.connect(lambda: self.dblsidedtool.run(toggle=True))
|
||||
self.ui.cal_btn.triggered.connect(lambda: self.cal_exc_tool.run(toggle=True))
|
||||
self.ui.cutout_btn.triggered.connect(lambda: self.cutout_tool.run(toggle=True))
|
||||
self.ui.ncc_btn.triggered.connect(lambda: self.ncclear_tool.run(toggle=True))
|
||||
self.ui.paint_btn.triggered.connect(lambda: self.paint_tool.run(toggle=True))
|
||||
|
@ -4237,7 +4254,7 @@ class App(QtCore.QObject):
|
|||
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"\
|
||||
"# ExportGerber, ExportSVG, Exteriors, Follow, GeoCutout, GeoUnion, GetNames,\n"\
|
||||
"# GetSys, ImportSvg, Interiors, Isolate, JoinExcellon, JoinGeometry, " \
|
||||
"ListSys, MillDrills,\n"\
|
||||
"# MillSlots, Mirror, New, NewExcellon, NewGeometry, NewGerber, Nregions, " \
|
||||
|
@ -4297,23 +4314,41 @@ class App(QtCore.QObject):
|
|||
# self.inform.emit('[selected] %s created & selected: %s' %
|
||||
# (str(obj.kind).capitalize(), str(obj.options['name'])))
|
||||
if obj.kind == 'gerber':
|
||||
self.inform.emit(_('[selected] {kind} created/selected: <span style="color:{color};">{name}</span>').format(
|
||||
kind=obj.kind.capitalize(), color='green', name=str(obj.options['name'])))
|
||||
self.inform.emit('[selected] {kind} {tx}: <span style="color:{color};">{name}</span>'.format(
|
||||
kind=obj.kind.capitalize(),
|
||||
color='green',
|
||||
name=str(obj.options['name']), tx=_("created/selected"))
|
||||
)
|
||||
elif obj.kind == 'excellon':
|
||||
self.inform.emit(_('[selected] {kind} created/selected: <span style="color:{color};">{name}</span>').format(
|
||||
kind=obj.kind.capitalize(), color='brown', name=str(obj.options['name'])))
|
||||
self.inform.emit('[selected] {kind} {tx}: <span style="color:{color};">{name}</span>'.format(
|
||||
kind=obj.kind.capitalize(),
|
||||
color='brown',
|
||||
name=str(obj.options['name']), tx=_("created/selected"))
|
||||
)
|
||||
elif obj.kind == 'cncjob':
|
||||
self.inform.emit(_('[selected] {kind} created/selected: <span style="color:{color};">{name}</span>').format(
|
||||
kind=obj.kind.capitalize(), color='blue', name=str(obj.options['name'])))
|
||||
self.inform.emit('[selected] {kind} {tx}: <span style="color:{color};">{name}</span>'.format(
|
||||
kind=obj.kind.capitalize(),
|
||||
color='blue',
|
||||
name=str(obj.options['name']), tx=_("created/selected"))
|
||||
)
|
||||
elif obj.kind == 'geometry':
|
||||
self.inform.emit(_('[selected] {kind} created/selected: <span style="color:{color};">{name}</span>').format(
|
||||
kind=obj.kind.capitalize(), color='red', name=str(obj.options['name'])))
|
||||
self.inform.emit('[selected] {kind} {tx}: <span style="color:{color};">{name}</span>'.format(
|
||||
kind=obj.kind.capitalize(),
|
||||
color='red',
|
||||
name=str(obj.options['name']), tx=_("created/selected"))
|
||||
)
|
||||
elif obj.kind == 'script':
|
||||
self.inform.emit(_('[selected] {kind} created/selected: <span style="color:{color};">{name}</span>').format(
|
||||
kind=obj.kind.capitalize(), color='orange', name=str(obj.options['name'])))
|
||||
self.inform.emit('[selected] {kind} {tx}: <span style="color:{color};">{name}</span>'.format(
|
||||
kind=obj.kind.capitalize(),
|
||||
color='orange',
|
||||
name=str(obj.options['name']), tx=_("created/selected"))
|
||||
)
|
||||
elif obj.kind == 'document':
|
||||
self.inform.emit(_('[selected] {kind} created/selected: <span style="color:{color};">{name}</span>').format(
|
||||
kind=obj.kind.capitalize(), color='darkCyan', name=str(obj.options['name'])))
|
||||
self.inform.emit('[selected] {kind} {tx}: <span style="color:{color};">{name}</span>'.format(
|
||||
kind=obj.kind.capitalize(),
|
||||
color='darkCyan',
|
||||
name=str(obj.options['name']), tx=_("created/selected"))
|
||||
)
|
||||
|
||||
# update the SHELL auto-completer model with the name of the new object
|
||||
self.shell._edit.set_model_data(self.myKeywords)
|
||||
|
@ -4468,10 +4503,11 @@ class App(QtCore.QObject):
|
|||
attributions_label = QtWidgets.QLabel(
|
||||
_(
|
||||
'Some of the icons used are from the following sources:<br>'
|
||||
'<div>Icons made by <a href="https://www.flaticon.com/authors/freepik" '
|
||||
'<div>Icons by <a href="https://www.flaticon.com/authors/freepik" '
|
||||
'title="Freepik">Freepik</a> from <a href="https://www.flaticon.com/" '
|
||||
'title="Flaticon">www.flaticon.com</a></div>'
|
||||
'Icons by <a target="_blank" href="https://icons8.com">Icons8</a>'
|
||||
'<div>Icons by <a target="_blank" href="https://icons8.com">Icons8</a></div>'
|
||||
'Icons by <a href="http://www.onlinewebfonts.com">oNline Web Fonts</a>'
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -7346,7 +7382,11 @@ class App(QtCore.QObject):
|
|||
|
||||
canvas_origin = self.plotcanvas.native.mapToGlobal(QtCore.QPoint(0, 0))
|
||||
jump_loc = self.plotcanvas.translate_coords_2((cal_location[0], cal_location[1]))
|
||||
j_pos = (canvas_origin.x() + jump_loc[0], (canvas_origin.y() + jump_loc[1]))
|
||||
|
||||
j_pos = (
|
||||
int(canvas_origin.x() + round(jump_loc[0])),
|
||||
int(canvas_origin.y() + round(jump_loc[1]))
|
||||
)
|
||||
cursor.setPos(j_pos[0], j_pos[1])
|
||||
else:
|
||||
# find the canvas origin which is in the top left corner
|
||||
|
@ -7358,10 +7398,15 @@ class App(QtCore.QObject):
|
|||
# in pixels where the origin 0,0 is in the lowest left point of the display window (in our case is the
|
||||
# canvas) and the point (width, height) is in the top-right location
|
||||
loc = self.plotcanvas.axes.transData.transform_point(location)
|
||||
j_pos = (x0 + loc[0], y0 - loc[1])
|
||||
j_pos = (
|
||||
int(x0 + loc[0]),
|
||||
int(y0 - loc[1])
|
||||
)
|
||||
cursor.setPos(j_pos[0], j_pos[1])
|
||||
self.plotcanvas.mouse = [location[0], location[1]]
|
||||
self.plotcanvas.draw_cursor(x_pos=location[0], y_pos=location[1])
|
||||
|
||||
if self.grid_status() == True:
|
||||
if self.grid_status():
|
||||
# Update cursor
|
||||
self.app_cursor.set_data(np.asarray([(location[0], location[1])]),
|
||||
symbol='++', edge_color=self.cursor_color_3D,
|
||||
|
@ -7376,8 +7421,7 @@ class App(QtCore.QObject):
|
|||
self.ui.rel_position_label.setText("<b>Dx</b>: %.4f <b>Dy</b>: "
|
||||
"%.4f " % (dx, dy))
|
||||
|
||||
self.inform.emit('[success] %s' %
|
||||
_("Done."))
|
||||
self.inform.emit('[success] %s' % _("Done."))
|
||||
return location
|
||||
|
||||
def on_copy_object(self):
|
||||
|
@ -8431,23 +8475,17 @@ class App(QtCore.QObject):
|
|||
was clicked, the pixel coordinates and the axes coordinates.
|
||||
:return: None
|
||||
"""
|
||||
self.pos = []
|
||||
self.pos = list()
|
||||
|
||||
if self.is_legacy is False:
|
||||
event_pos = event.pos
|
||||
if self.defaults["global_pan_button"] == '2':
|
||||
pan_button = 2
|
||||
else:
|
||||
pan_button = 3
|
||||
pan_button = 2 if self.defaults["global_pan_button"] == '2'else 3
|
||||
# Set the mouse button for panning
|
||||
self.plotcanvas.view.camera.pan_button_setting = pan_button
|
||||
else:
|
||||
event_pos = (event.xdata, event.ydata)
|
||||
# Matplotlib has the middle and right buttons mapped in reverse compared with VisPy
|
||||
if self.defaults["global_pan_button"] == '2':
|
||||
pan_button = 3
|
||||
else:
|
||||
pan_button = 2
|
||||
pan_button = 3 if self.defaults["global_pan_button"] == '2'else 2
|
||||
|
||||
# So it can receive key presses
|
||||
self.plotcanvas.native.setFocus()
|
||||
|
@ -8836,17 +8874,29 @@ class App(QtCore.QObject):
|
|||
def selected_message(self, curr_sel_obj):
|
||||
if curr_sel_obj:
|
||||
if curr_sel_obj.kind == 'gerber':
|
||||
self.inform.emit(_('[selected]<span style="color:{color};">{name}</span> selected').format(
|
||||
color='green', name=str(curr_sel_obj.options['name'])))
|
||||
self.inform.emit('[selected]<span style="color:{color};">{name}</span> {tx}'.format(
|
||||
color='green',
|
||||
name=str(curr_sel_obj.options['name']),
|
||||
tx=_("selected"))
|
||||
)
|
||||
elif curr_sel_obj.kind == 'excellon':
|
||||
self.inform.emit(_('[selected]<span style="color:{color};">{name}</span> selected').format(
|
||||
color='brown', name=str(curr_sel_obj.options['name'])))
|
||||
self.inform.emit('[selected]<span style="color:{color};">{name}</span> {tx}'.format(
|
||||
color='brown',
|
||||
name=str(curr_sel_obj.options['name']),
|
||||
tx=_("selected"))
|
||||
)
|
||||
elif curr_sel_obj.kind == 'cncjob':
|
||||
self.inform.emit(_('[selected]<span style="color:{color};">{name}</span> selected').format(
|
||||
color='blue', name=str(curr_sel_obj.options['name'])))
|
||||
self.inform.emit('[selected]<span style="color:{color};">{name}</span> {tx}'.format(
|
||||
color='blue',
|
||||
name=str(curr_sel_obj.options['name']),
|
||||
tx=_("selected"))
|
||||
)
|
||||
elif curr_sel_obj.kind == 'geometry':
|
||||
self.inform.emit(_('[selected]<span style="color:{color};">{name}</span> selected').format(
|
||||
color='red', name=str(curr_sel_obj.options['name'])))
|
||||
self.inform.emit('[selected]<span style="color:{color};">{name}</span> {tx}'.format(
|
||||
color='red',
|
||||
name=str(curr_sel_obj.options['name']),
|
||||
tx=_("selected"))
|
||||
)
|
||||
|
||||
def delete_hover_shape(self):
|
||||
self.hover_shapes.clear()
|
||||
|
@ -9217,7 +9267,7 @@ class App(QtCore.QObject):
|
|||
|
||||
# https://bobcadsupport.com/helpdesk/index.php?/Knowledgebase/Article/View/13/5/known-g-code-file-extensions
|
||||
_filter_ = "G-Code Files (*.txt *.nc *.ncc *.tap *.gcode *.cnc *.ecs *.fnc *.dnc *.ncg *.gc *.fan *.fgc" \
|
||||
" *.din *.xpi *.hnc *.h *.i *.ncp *.min *.gcd *.rol *.mpr *.ply *.out *.eia *.plt *.sbp *.mpf);;" \
|
||||
" *.din *.xpi *.hnc *.h *.i *.ncp *.min *.gcd *.rol *.mpr *.ply *.out *.eia *.sbp *.mpf);;" \
|
||||
"All Files (*.*)"
|
||||
|
||||
if name is None:
|
||||
|
@ -9277,6 +9327,44 @@ class App(QtCore.QObject):
|
|||
# thread safe. The new_project()
|
||||
self.open_project(filename)
|
||||
|
||||
def on_fileopenhpgl2(self, signal: bool = None, name=None):
|
||||
"""
|
||||
File menu callback for opening a HPGL2.
|
||||
|
||||
:param signal: required because clicking the entry will generate a checked signal which needs a container
|
||||
:return: None
|
||||
"""
|
||||
|
||||
self.report_usage("on_fileopenhpgl2")
|
||||
App.log.debug("on_fileopenhpgl2()")
|
||||
|
||||
_filter_ = "HPGL2 Files (*.plt);;" \
|
||||
"All Files (*.*)"
|
||||
|
||||
if name is None:
|
||||
try:
|
||||
filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open HPGL2"),
|
||||
directory=self.get_last_folder(),
|
||||
filter=_filter_)
|
||||
except TypeError:
|
||||
filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open HPGL2"), filter=_filter_)
|
||||
|
||||
filenames = [str(filename) for filename in filenames]
|
||||
else:
|
||||
filenames = [name]
|
||||
self.splash.showMessage('%s: %ssec\n%s' % (_("Canvas initialization started.\n"
|
||||
"Canvas initialization finished in"), '%.2f' % self.used_time,
|
||||
_("Opening HPGL2 file.")),
|
||||
alignment=Qt.AlignBottom | Qt.AlignLeft,
|
||||
color=QtGui.QColor("gray"))
|
||||
|
||||
if len(filenames) == 0:
|
||||
self.inform.emit('[WARNING_NOTCL] %s' % _("Open HPGL2 file cancelled."))
|
||||
else:
|
||||
for filename in filenames:
|
||||
if filename != '':
|
||||
self.worker_task.emit({'fcn': self.open_hpgl2, 'params': [filename]})
|
||||
|
||||
def on_file_openconfig(self, signal: bool = None):
|
||||
"""
|
||||
File menu callback for opening a config file.
|
||||
|
@ -10108,23 +10196,25 @@ class App(QtCore.QObject):
|
|||
try:
|
||||
filename, _f = QtWidgets.QFileDialog.getSaveFileName(
|
||||
caption=_("Save Project As ..."),
|
||||
directory=_('{l_save}/Project_{date}').format(l_save=str(self.get_last_save_folder()), date=self.date),
|
||||
filter=filter_)
|
||||
directory=('{l_save}/{proj}_{date}').format(l_save=str(self.get_last_save_folder()), date=self.date,
|
||||
proj=_("Project")),
|
||||
filter=filter_
|
||||
)
|
||||
except TypeError:
|
||||
filename, _f = QtWidgets.QFileDialog.getSaveFileName(caption=_("Save Project As ..."), filter=filter_)
|
||||
|
||||
filename = str(filename)
|
||||
|
||||
if filename == '':
|
||||
self.inform.emit('[WARNING_NOTCL] %s' %
|
||||
_("Save Project cancelled."))
|
||||
self.inform.emit('[WARNING_NOTCL] %s' % _("Save Project cancelled."))
|
||||
return
|
||||
|
||||
try:
|
||||
f = open(filename, 'r')
|
||||
f.close()
|
||||
except IOError:
|
||||
pass
|
||||
self.inform.emit('[ERROR_NOTCL] %s' % _("The object is used by another application."))
|
||||
return
|
||||
|
||||
if use_thread is True:
|
||||
self.worker_task.emit({'fcn': self.save_project,
|
||||
|
@ -10143,6 +10233,50 @@ class App(QtCore.QObject):
|
|||
self.set_ui_title(name=self.project_filename)
|
||||
self.should_we_save = False
|
||||
|
||||
def on_file_save_object_pdf(self, use_thread=True):
|
||||
self.date = str(datetime.today()).rpartition('.')[0]
|
||||
self.date = ''.join(c for c in self.date if c not in ':-')
|
||||
self.date = self.date.replace(' ', '_')
|
||||
|
||||
try:
|
||||
obj_active = self.collection.get_active()
|
||||
obj_name = _(str(obj_active.options['name']))
|
||||
except AttributeError as err:
|
||||
log.debug("App.on_file_save_object_pdf() --> %s" % str(err))
|
||||
self.inform.emit('[ERROR_NOTCL] %s' % _("No object selected."))
|
||||
return
|
||||
|
||||
filter_ = "PDF File (*.PDF);; All Files (*.*)"
|
||||
try:
|
||||
filename, _f = QtWidgets.QFileDialog.getSaveFileName(
|
||||
caption=_("Save Object as PDF ..."),
|
||||
directory=('{l_save}/{obj_name}_{date}').format(l_save=str(self.get_last_save_folder()),
|
||||
obj_name=obj_name,
|
||||
date=self.date),
|
||||
filter=filter_
|
||||
)
|
||||
except TypeError:
|
||||
filename, _f = QtWidgets.QFileDialog.getSaveFileName(caption=_("Save Object as PDF ..."), filter=filter_)
|
||||
|
||||
filename = str(filename)
|
||||
|
||||
if filename == '':
|
||||
self.inform.emit('[WARNING_NOTCL] %s' % _("Save Object PDF cancelled."))
|
||||
return
|
||||
|
||||
if use_thread is True:
|
||||
self.worker_task.emit({'fcn': self.save_pdf, 'params': [filename, obj_name]})
|
||||
else:
|
||||
self.save_pdf(filename, obj_name)
|
||||
|
||||
# self.save_project(filename)
|
||||
if self.defaults["global_open_style"] is False:
|
||||
self.file_opened.emit("pdf", filename)
|
||||
self.file_saved.emit("pdf", filename)
|
||||
|
||||
def save_pdf(self, file_name, obj_name):
|
||||
self.film_tool.export_positive(obj_name=obj_name, box_name=obj_name, filename=file_name, ftype='pdf')
|
||||
|
||||
def export_svg(self, obj_name, filename, scale_stroke_factor=0.00):
|
||||
"""
|
||||
Exports a Geometry Object to an SVG file.
|
||||
|
@ -10870,6 +11004,73 @@ class App(QtCore.QObject):
|
|||
self.inform.emit('[success] %s: %s' %
|
||||
(_("Opened"), filename))
|
||||
|
||||
def open_hpgl2(self, filename, outname=None):
|
||||
"""
|
||||
Opens a HPGL2 file, parses it and creates a new object for
|
||||
it in the program. Thread-safe.
|
||||
|
||||
:param outname: Name of the resulting object. None causes the
|
||||
name to be that of the file.
|
||||
:param filename: HPGL2 file filename
|
||||
:type filename: str
|
||||
:return: None
|
||||
"""
|
||||
filename = filename
|
||||
|
||||
# How the object should be initialized
|
||||
def obj_init(geo_obj, app_obj):
|
||||
|
||||
assert isinstance(geo_obj, FlatCAMGeometry), \
|
||||
"Expected to initialize a FlatCAMGeometry but got %s" % type(geo_obj)
|
||||
|
||||
# Opening the file happens here
|
||||
obj = HPGL2(self)
|
||||
try:
|
||||
HPGL2.parse_file(obj, 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_hpgl2() --> %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"
|
||||
|
||||
geo_obj.multigeo = True
|
||||
geo_obj.solid_geometry = deepcopy(obj.solid_geometry)
|
||||
geo_obj.tools = deepcopy(obj.tools)
|
||||
geo_obj.source_file = deepcopy(obj.source_file)
|
||||
|
||||
del obj
|
||||
|
||||
if not geo_obj.solid_geometry:
|
||||
app_obj.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_("Object is not HPGL2 file or empty. Aborting object creation."))
|
||||
return "fail"
|
||||
|
||||
App.log.debug("open_hpgl2()")
|
||||
|
||||
with self.proc_container.new(_("Opening HPGL2")) as proc:
|
||||
# Object name
|
||||
name = outname or filename.split('/')[-1].split('\\')[-1]
|
||||
|
||||
# # ## Object creation # ##
|
||||
ret = self.new_object("geometry", name, obj_init, autoselected=False)
|
||||
if ret == 'fail':
|
||||
self.inform.emit('[ERROR_NOTCL]%s' % _(' Open HPGL2 failed. Probable not a HPGL2 file.'))
|
||||
return 'fail'
|
||||
|
||||
# Register recent file
|
||||
self.file_opened.emit("geometry", filename)
|
||||
|
||||
# GUI feedback
|
||||
self.inform.emit('[success] %s: %s' % (_("Opened"), filename))
|
||||
|
||||
def open_script(self, filename, outname=None, silent=False):
|
||||
"""
|
||||
Opens a Script file, parses it and creates a new object for
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
# ##########################################################
|
||||
|
||||
from PyQt5 import QtGui, QtCore, QtWidgets
|
||||
from flatcamGUI.GUIElements import FCTable, FCEntry, FCButton, FCDoubleSpinner, FCComboBox, FCCheckBox
|
||||
from flatcamGUI.GUIElements import FCTable, FCEntry, FCButton, FCDoubleSpinner, FCComboBox, FCCheckBox, FCSpinner
|
||||
from camlib import to_dict
|
||||
|
||||
import sys
|
||||
|
@ -505,7 +505,7 @@ class ToolsDB(QtWidgets.QWidget):
|
|||
self.table_widget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
||||
table_hlay.addWidget(self.table_widget)
|
||||
|
||||
self.table_widget.setColumnCount(26)
|
||||
self.table_widget.setColumnCount(27)
|
||||
# self.table_widget.setColumnWidth(0, 20)
|
||||
self.table_widget.setHorizontalHeaderLabels(
|
||||
[
|
||||
|
@ -530,6 +530,7 @@ class ToolsDB(QtWidgets.QWidget):
|
|||
_("Dwelltime"),
|
||||
_("Preprocessor"),
|
||||
_("ExtraCut"),
|
||||
_("E-Cut Length"),
|
||||
_("Toolchange"),
|
||||
_("Toolchange XY"),
|
||||
_("Toolchange Z"),
|
||||
|
@ -620,23 +621,30 @@ class ToolsDB(QtWidgets.QWidget):
|
|||
"such as that this point is covered by this extra cut to\n"
|
||||
"ensure a complete isolation."))
|
||||
self.table_widget.horizontalHeaderItem(21).setToolTip(
|
||||
_("Extra Cut length.\n"
|
||||
"If checked, after a isolation is finished an extra cut\n"
|
||||
"will be added where the start and end of isolation meet\n"
|
||||
"such as that this point is covered by this extra cut to\n"
|
||||
"ensure a complete isolation. This is the length of\n"
|
||||
"the extra cut."))
|
||||
self.table_widget.horizontalHeaderItem(22).setToolTip(
|
||||
_("Toolchange.\n"
|
||||
"It will create a toolchange event.\n"
|
||||
"The kind of toolchange is determined by\n"
|
||||
"the preprocessor file."))
|
||||
self.table_widget.horizontalHeaderItem(22).setToolTip(
|
||||
self.table_widget.horizontalHeaderItem(23).setToolTip(
|
||||
_("Toolchange XY.\n"
|
||||
"A set of coordinates in the format (x, y).\n"
|
||||
"Will determine the cartesian position of the point\n"
|
||||
"where the tool change event take place."))
|
||||
self.table_widget.horizontalHeaderItem(23).setToolTip(
|
||||
self.table_widget.horizontalHeaderItem(24).setToolTip(
|
||||
_("Toolchange Z.\n"
|
||||
"The position on Z plane where the tool change event take place."))
|
||||
self.table_widget.horizontalHeaderItem(24).setToolTip(
|
||||
self.table_widget.horizontalHeaderItem(25).setToolTip(
|
||||
_("Start Z.\n"
|
||||
"If it's left empty it will not be used.\n"
|
||||
"A position on Z plane to move immediately after job start."))
|
||||
self.table_widget.horizontalHeaderItem(25).setToolTip(
|
||||
self.table_widget.horizontalHeaderItem(26).setToolTip(
|
||||
_("End Z.\n"
|
||||
"A position on Z plane to move immediately after job stop."))
|
||||
|
||||
|
@ -840,6 +848,16 @@ class ToolsDB(QtWidgets.QWidget):
|
|||
multidepth_item.set_value(data['multidepth'])
|
||||
widget.setCellWidget(row, 8, multidepth_item)
|
||||
|
||||
# to make the checkbox centered but it can no longer have it's value accessed - needs a fix using findchild()
|
||||
# multidepth_item = QtWidgets.QWidget()
|
||||
# cb = FCCheckBox()
|
||||
# cb.set_value(data['multidepth'])
|
||||
# qhboxlayout = QtWidgets.QHBoxLayout(multidepth_item)
|
||||
# qhboxlayout.addWidget(cb)
|
||||
# qhboxlayout.setAlignment(QtCore.Qt.AlignCenter)
|
||||
# qhboxlayout.setContentsMargins(0, 0, 0, 0)
|
||||
# widget.setCellWidget(row, 8, multidepth_item)
|
||||
|
||||
depth_per_pass_item = FCDoubleSpinner()
|
||||
depth_per_pass_item.set_precision(self.decimals)
|
||||
depth_per_pass_item.setSingleStep(0.1)
|
||||
|
@ -890,8 +908,11 @@ class ToolsDB(QtWidgets.QWidget):
|
|||
frrapids_item.set_value(float(data['feedrate_rapid']))
|
||||
widget.setCellWidget(row, 15, frrapids_item)
|
||||
|
||||
spindlespeed_item = QtWidgets.QTableWidgetItem(str(data['spindlespeed']) if data['spindlespeed'] else '')
|
||||
widget.setItem(row, 16, spindlespeed_item)
|
||||
spindlespeed_item = FCSpinner()
|
||||
spindlespeed_item.set_range(0, 1000000)
|
||||
spindlespeed_item.set_value(int(data['spindlespeed']))
|
||||
spindlespeed_item.setSingleStep(100)
|
||||
widget.setCellWidget(row, 16, spindlespeed_item)
|
||||
|
||||
dwell_item = FCCheckBox()
|
||||
dwell_item.set_value(data['dwell'])
|
||||
|
@ -913,12 +934,18 @@ class ToolsDB(QtWidgets.QWidget):
|
|||
ecut_item.set_value(data['extracut'])
|
||||
widget.setCellWidget(row, 20, ecut_item)
|
||||
|
||||
ecut_length_item = FCDoubleSpinner()
|
||||
ecut_length_item.set_precision(self.decimals)
|
||||
ecut_length_item.set_range(0.0, 9999.9999)
|
||||
ecut_length_item.set_value(data['extracut_length'])
|
||||
widget.setCellWidget(row, 21, ecut_length_item)
|
||||
|
||||
toolchange_item = FCCheckBox()
|
||||
toolchange_item.set_value(data['toolchange'])
|
||||
widget.setCellWidget(row, 21, toolchange_item)
|
||||
widget.setCellWidget(row, 22, toolchange_item)
|
||||
|
||||
toolchangexy_item = QtWidgets.QTableWidgetItem(str(data['toolchangexy']) if data['toolchangexy'] else '')
|
||||
widget.setItem(row, 22, toolchangexy_item)
|
||||
widget.setItem(row, 23, toolchangexy_item)
|
||||
|
||||
toolchangez_item = FCDoubleSpinner()
|
||||
toolchangez_item.set_precision(self.decimals)
|
||||
|
@ -929,10 +956,10 @@ class ToolsDB(QtWidgets.QWidget):
|
|||
toolchangez_item.set_range(0.0000, 9999.9999)
|
||||
|
||||
toolchangez_item.set_value(float(data['toolchangez']))
|
||||
widget.setCellWidget(row, 23, toolchangez_item)
|
||||
widget.setCellWidget(row, 24, toolchangez_item)
|
||||
|
||||
startz_item = QtWidgets.QTableWidgetItem(str(data['startz']) if data['startz'] else '')
|
||||
widget.setItem(row, 24, startz_item)
|
||||
widget.setItem(row, 25, startz_item)
|
||||
|
||||
endz_item = FCDoubleSpinner()
|
||||
endz_item.set_precision(self.decimals)
|
||||
|
@ -943,7 +970,7 @@ class ToolsDB(QtWidgets.QWidget):
|
|||
endz_item.set_range(0.0000, 9999.9999)
|
||||
|
||||
endz_item.set_value(float(data['endz']))
|
||||
widget.setCellWidget(row, 25, endz_item)
|
||||
widget.setCellWidget(row, 26, endz_item)
|
||||
|
||||
def on_tool_add(self):
|
||||
"""
|
||||
|
@ -970,6 +997,7 @@ class ToolsDB(QtWidgets.QWidget):
|
|||
"dwelltime": float(self.app.defaults["geometry_dwelltime"]),
|
||||
"ppname_g": self.app.defaults["geometry_ppname_g"],
|
||||
"extracut": self.app.defaults["geometry_extracut"],
|
||||
"extracut_length": 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"]),
|
||||
|
@ -1257,8 +1285,7 @@ class ToolsDB(QtWidgets.QWidget):
|
|||
elif column_header_text == 'FR Rapids':
|
||||
default_data['feedrate_rapid'] = self.table_widget.cellWidget(row, col).get_value()
|
||||
elif column_header_text == 'Spindle Speed':
|
||||
default_data['spindlespeed'] = float(self.table_widget.item(row, col).text()) \
|
||||
if self.table_widget.item(row, col).text() is not '' else None
|
||||
default_data['spindlespeed'] = self.table_widget.cellWidget(row, col).get_value()
|
||||
elif column_header_text == 'Dwell':
|
||||
default_data['dwell'] = self.table_widget.cellWidget(row, col).get_value()
|
||||
elif column_header_text == 'Dwelltime':
|
||||
|
@ -1267,6 +1294,8 @@ class ToolsDB(QtWidgets.QWidget):
|
|||
default_data['ppname_g'] = self.table_widget.cellWidget(row, col).get_value()
|
||||
elif column_header_text == 'ExtraCut':
|
||||
default_data['extracut'] = self.table_widget.cellWidget(row, col).get_value()
|
||||
elif column_header_text == "E-Cut Length":
|
||||
default_data['extracut_length'] = self.table_widget.cellWidget(row, col).get_value()
|
||||
elif column_header_text == 'Toolchange':
|
||||
default_data['toolchange'] = self.table_widget.cellWidget(row, col).get_value()
|
||||
elif column_header_text == 'Toolchange XY':
|
||||
|
|
340
FlatCAMObj.py
340
FlatCAMObj.py
|
@ -195,10 +195,11 @@ class FlatCAMObj(QtCore.QObject):
|
|||
pass
|
||||
|
||||
# Creates problems on focusOut
|
||||
# try:
|
||||
# self.ui.scale_entry.returnPressed.connect(self.on_scale_button_click)
|
||||
# except (TypeError, AttributeError):
|
||||
# pass
|
||||
try:
|
||||
self.ui.scale_entry.returnPressed.connect(self.on_scale_button_click)
|
||||
except (TypeError, AttributeError):
|
||||
pass
|
||||
|
||||
# self.ui.skew_button.clicked.connect(self.on_skew_button_click)
|
||||
|
||||
def build_ui(self):
|
||||
|
@ -267,9 +268,19 @@ class FlatCAMObj(QtCore.QObject):
|
|||
|
||||
def on_scale_button_click(self):
|
||||
self.read_form()
|
||||
factor = self.ui.scale_entry.get_value()
|
||||
try:
|
||||
factor = float(eval(self.ui.scale_entry.get_value()))
|
||||
except Exception as e:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _("Scaling could not be executed."))
|
||||
log.debug("FlatCAMObj.on_scale_button_click() -- %s" % str(e))
|
||||
return
|
||||
|
||||
if type(factor) != float:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _("Scaling could not be executed."))
|
||||
|
||||
# if factor is 1.0 do nothing, there is no point in scaling with a factor of 1.0
|
||||
if factor == 1.0:
|
||||
self.app.inform.emit('[success] %s' % _("Scale done."))
|
||||
return
|
||||
|
||||
log.debug("FlatCAMObj.on_scale_button_click()")
|
||||
|
@ -277,6 +288,8 @@ class FlatCAMObj(QtCore.QObject):
|
|||
def worker_task():
|
||||
with self.app.proc_container.new(_("Scaling...")):
|
||||
self.scale(factor)
|
||||
self.app.inform.emit('[success] %s' % _("Scale done."))
|
||||
|
||||
self.app.proc_container.update_view_text('')
|
||||
with self.app.proc_container.new('%s...' % _("Plotting")):
|
||||
self.plot()
|
||||
|
@ -625,6 +638,8 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
|
||||
# Mouse events
|
||||
self.mr = None
|
||||
self.mm = None
|
||||
self.mp = None
|
||||
|
||||
# dict to store the polygons selected for isolation; key is the shape added to be plotted and value is the poly
|
||||
self.poly_dict = dict()
|
||||
|
@ -1066,11 +1081,11 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
if self.app.is_legacy is False:
|
||||
event_pos = event.pos
|
||||
right_button = 2
|
||||
event_is_dragging = self.app.event_is_dragging
|
||||
self.app.event_is_dragging = self.app.event_is_dragging
|
||||
else:
|
||||
event_pos = (event.xdata, event.ydata)
|
||||
right_button = 3
|
||||
event_is_dragging = self.app.ui.popMenu.mouse_is_panning
|
||||
self.app.event_is_dragging = self.app.ui.popMenu.mouse_is_panning
|
||||
|
||||
try:
|
||||
x = float(event_pos[0])
|
||||
|
@ -1080,11 +1095,18 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
|
||||
event_pos = (x, y)
|
||||
curr_pos = self.app.plotcanvas.translate_coords(event_pos)
|
||||
if self.app.grid_status():
|
||||
curr_pos = self.app.geo_editor.snap(curr_pos[0], curr_pos[1])
|
||||
else:
|
||||
curr_pos = (curr_pos[0], curr_pos[1])
|
||||
|
||||
if event.button == 1:
|
||||
clicked_poly = self.find_polygon(point=(curr_pos[0], curr_pos[1]))
|
||||
|
||||
if clicked_poly:
|
||||
if self.app.selection_type is not None:
|
||||
self.selection_area_handler(self.app.pos, curr_pos, self.app.selection_type)
|
||||
self.app.selection_type = None
|
||||
elif clicked_poly:
|
||||
if clicked_poly not in self.poly_dict.values():
|
||||
shape_id = self.app.tool_shapes.add(tolerance=self.drawing_tolerance, layer=0, shape=clicked_poly,
|
||||
color=self.app.defaults['global_sel_draw_color'] + 'AF',
|
||||
|
@ -1113,8 +1135,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
self.app.tool_shapes.redraw()
|
||||
else:
|
||||
self.app.inform.emit(_("No polygon detected under click position."))
|
||||
|
||||
elif event.button == right_button and event_is_dragging is False:
|
||||
elif event.button == right_button and self.app.event_is_dragging is False:
|
||||
# restore the Grid snapping if it was active before
|
||||
if self.grid_status_memory is True:
|
||||
self.app.ui.grid_snap_btn.trigger()
|
||||
|
@ -1136,6 +1157,75 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
else:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _("List of single polygons is empty. Aborting."))
|
||||
|
||||
def selection_area_handler(self, start_pos, end_pos, sel_type):
|
||||
"""
|
||||
:param start_pos: mouse position when the selection LMB click was done
|
||||
:param end_pos: mouse position when the left mouse button is released
|
||||
:param sel_type: if True it's a left to right selection (enclosure), if False it's a 'touch' selection
|
||||
:return:
|
||||
"""
|
||||
poly_selection = Polygon([start_pos, (end_pos[0], start_pos[1]), end_pos, (start_pos[0], end_pos[1])])
|
||||
|
||||
# delete previous selection shape
|
||||
self.app.delete_selection_shape()
|
||||
|
||||
added_poly_count = 0
|
||||
try:
|
||||
for geo in self.solid_geometry:
|
||||
if geo not in self.poly_dict.values():
|
||||
if sel_type is True:
|
||||
if geo.within(poly_selection):
|
||||
shape_id = self.app.tool_shapes.add(tolerance=self.drawing_tolerance, layer=0,
|
||||
shape=geo,
|
||||
color=self.app.defaults['global_sel_draw_color'] + 'AF',
|
||||
face_color=self.app.defaults[
|
||||
'global_sel_draw_color'] + 'AF',
|
||||
visible=True)
|
||||
self.poly_dict[shape_id] = geo
|
||||
added_poly_count += 1
|
||||
else:
|
||||
if poly_selection.intersects(geo):
|
||||
shape_id = self.app.tool_shapes.add(tolerance=self.drawing_tolerance, layer=0,
|
||||
shape=geo,
|
||||
color=self.app.defaults['global_sel_draw_color'] + 'AF',
|
||||
face_color=self.app.defaults[
|
||||
'global_sel_draw_color'] + 'AF',
|
||||
visible=True)
|
||||
self.poly_dict[shape_id] = geo
|
||||
added_poly_count += 1
|
||||
except TypeError:
|
||||
if self.solid_geometry not in self.poly_dict.values():
|
||||
if sel_type is True:
|
||||
if self.solid_geometry.within(poly_selection):
|
||||
shape_id = self.app.tool_shapes.add(tolerance=self.drawing_tolerance, layer=0,
|
||||
shape=self.solid_geometry,
|
||||
color=self.app.defaults['global_sel_draw_color'] + 'AF',
|
||||
face_color=self.app.defaults[
|
||||
'global_sel_draw_color'] + 'AF',
|
||||
visible=True)
|
||||
self.poly_dict[shape_id] = self.solid_geometry
|
||||
added_poly_count += 1
|
||||
else:
|
||||
if poly_selection.intersects(self.solid_geometry):
|
||||
shape_id = self.app.tool_shapes.add(tolerance=self.drawing_tolerance, layer=0,
|
||||
shape=self.solid_geometry,
|
||||
color=self.app.defaults['global_sel_draw_color'] + 'AF',
|
||||
face_color=self.app.defaults[
|
||||
'global_sel_draw_color'] + 'AF',
|
||||
visible=True)
|
||||
self.poly_dict[shape_id] = self.solid_geometry
|
||||
added_poly_count += 1
|
||||
|
||||
if added_poly_count > 0:
|
||||
self.app.tool_shapes.redraw()
|
||||
self.app.inform.emit(
|
||||
'%s: %d. %s' % (_("Added polygon"),
|
||||
int(added_poly_count),
|
||||
_("Click to add next polygon or right click to start isolation."))
|
||||
)
|
||||
else:
|
||||
self.app.inform.emit(_("No polygon in selection."))
|
||||
|
||||
def isolate(self, iso_type=None, geometry=None, dia=None, passes=None, overlap=None, outname=None, combine=None,
|
||||
milling_type=None, follow=None, plot=True):
|
||||
"""
|
||||
|
@ -1242,6 +1332,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
"ppname_g": self.app.defaults['geometry_ppname_g'],
|
||||
"depthperpass": self.app.defaults['geometry_depthperpass'],
|
||||
"extracut": self.app.defaults['geometry_extracut'],
|
||||
"extracut_length": self.app.defaults['geometry_extracut_length'],
|
||||
"toolchange": self.app.defaults['geometry_toolchange'],
|
||||
"toolchangez": self.app.defaults['geometry_toolchangez'],
|
||||
"endz": self.app.defaults['geometry_endz'],
|
||||
|
@ -1718,18 +1809,25 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
:param aperture: string; aperture for which to clear the mark shapes
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
|
||||
if self.mark_shapes:
|
||||
if aperture == 'all':
|
||||
for apid in list(self.apertures.keys()):
|
||||
if self.app.is_legacy is True:
|
||||
self.mark_shapes[apid].clear(update=False)
|
||||
else:
|
||||
self.mark_shapes[apid].clear(update=True)
|
||||
|
||||
try:
|
||||
if self.app.is_legacy is True:
|
||||
self.mark_shapes[apid].clear(update=False)
|
||||
else:
|
||||
self.mark_shapes[apid].clear(update=True)
|
||||
except Exception as e:
|
||||
log.debug("FlatCAMGerber.clear_plot_apertures() 'all' --> %s" % str(e))
|
||||
else:
|
||||
self.mark_shapes[aperture].clear(update=True)
|
||||
except Exception as e:
|
||||
log.debug("FlatCAMGerber.clear_plot_apertures() --> %s" % str(e))
|
||||
try:
|
||||
if self.app.is_legacy is True:
|
||||
self.mark_shapes[aperture].clear(update=False)
|
||||
else:
|
||||
self.mark_shapes[aperture].clear(update=True)
|
||||
except Exception as e:
|
||||
log.debug("FlatCAMGerber.clear_plot_apertures() 'aperture' --> %s" % str(e))
|
||||
|
||||
def clear_mark_all(self):
|
||||
self.ui.mark_all_cb.set_value(False)
|
||||
|
@ -2117,7 +2215,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
|
|||
"toolchangexy": "0.0, 0.0",
|
||||
"endz": 2.0,
|
||||
"startz": None,
|
||||
"spindlespeed": None,
|
||||
"spindlespeed": 0,
|
||||
"dwell": True,
|
||||
"dwelltime": 1000,
|
||||
"ppname_e": 'defaults',
|
||||
|
@ -2128,10 +2226,10 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
|
|||
})
|
||||
|
||||
# TODO: Document this.
|
||||
self.tool_cbs = {}
|
||||
self.tool_cbs = dict()
|
||||
|
||||
# dict to hold the tool number as key and tool offset as value
|
||||
self.tool_offset = {}
|
||||
self.tool_offset = dict()
|
||||
|
||||
# variable to store the total amount of drills per job
|
||||
self.tot_drill_cnt = 0
|
||||
|
@ -3175,7 +3273,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
|
|||
job_obj.feedrate = float(self.options["feedrate"])
|
||||
job_obj.feedrate_rapid = float(self.options["feedrate_rapid"])
|
||||
|
||||
job_obj.spindlespeed = float(self.options["spindlespeed"]) if self.options["spindlespeed"] else None
|
||||
job_obj.spindlespeed = float(self.options["spindlespeed"]) if self.options["spindlespeed"] != 0 else None
|
||||
job_obj.spindledir = self.app.defaults['excellon_spindledir']
|
||||
job_obj.dwell = self.options["dwell"]
|
||||
job_obj.dwelltime = float(self.options["dwelltime"])
|
||||
|
@ -3254,30 +3352,31 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
|
|||
def convert_units(self, units):
|
||||
log.debug("FlatCAMObj.FlatCAMExcellon.convert_units()")
|
||||
|
||||
factor = Excellon.convert_units(self, units)
|
||||
Excellon.convert_units(self, units)
|
||||
|
||||
self.options['drillz'] = float(self.options['drillz']) * factor
|
||||
self.options['travelz'] = float(self.options['travelz']) * factor
|
||||
self.options['feedrate'] = float(self.options['feedrate']) * factor
|
||||
self.options['feedrate_rapid'] = float(self.options['feedrate_rapid']) * factor
|
||||
self.options['toolchangez'] = float(self.options['toolchangez']) * factor
|
||||
|
||||
if self.app.defaults["excellon_toolchangexy"] == '':
|
||||
self.options['toolchangexy'] = "0.0, 0.0"
|
||||
else:
|
||||
coords_xy = [float(eval(coord)) for coord in self.app.defaults["excellon_toolchangexy"].split(",")]
|
||||
if len(coords_xy) < 2:
|
||||
self.app.inform.emit('[ERROR] %s' % _("The Toolchange X,Y field in Edit -> Preferences has to be "
|
||||
"in the format (x, y) \n"
|
||||
"but now there is only one value, not two. "))
|
||||
return 'fail'
|
||||
coords_xy[0] *= factor
|
||||
coords_xy[1] *= factor
|
||||
self.options['toolchangexy'] = "%f, %f" % (coords_xy[0], coords_xy[1])
|
||||
|
||||
if self.options['startz'] is not None:
|
||||
self.options['startz'] = float(self.options['startz']) * factor
|
||||
self.options['endz'] = float(self.options['endz']) * factor
|
||||
# factor = Excellon.convert_units(self, units)
|
||||
# self.options['drillz'] = float(self.options['drillz']) * factor
|
||||
# self.options['travelz'] = float(self.options['travelz']) * factor
|
||||
# self.options['feedrate'] = float(self.options['feedrate']) * factor
|
||||
# self.options['feedrate_rapid'] = float(self.options['feedrate_rapid']) * factor
|
||||
# self.options['toolchangez'] = float(self.options['toolchangez']) * factor
|
||||
#
|
||||
# if self.app.defaults["excellon_toolchangexy"] == '':
|
||||
# self.options['toolchangexy'] = "0.0, 0.0"
|
||||
# else:
|
||||
# coords_xy = [float(eval(coord)) for coord in self.app.defaults["excellon_toolchangexy"].split(",")]
|
||||
# if len(coords_xy) < 2:
|
||||
# self.app.inform.emit('[ERROR] %s' % _("The Toolchange X,Y field in Edit -> Preferences has to be "
|
||||
# "in the format (x, y) \n"
|
||||
# "but now there is only one value, not two. "))
|
||||
# return 'fail'
|
||||
# coords_xy[0] *= factor
|
||||
# coords_xy[1] *= factor
|
||||
# self.options['toolchangexy'] = "%f, %f" % (coords_xy[0], coords_xy[1])
|
||||
#
|
||||
# if self.options['startz'] is not None:
|
||||
# self.options['startz'] = float(self.options['startz']) * factor
|
||||
# self.options['endz'] = float(self.options['endz']) * factor
|
||||
|
||||
def on_solid_cb_click(self, *args):
|
||||
if self.muted_ui:
|
||||
|
@ -3439,12 +3538,13 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
"feedrate": 5.0,
|
||||
"feedrate_z": 5.0,
|
||||
"feedrate_rapid": 5.0,
|
||||
"spindlespeed": None,
|
||||
"spindlespeed": 0,
|
||||
"dwell": True,
|
||||
"dwelltime": 1000,
|
||||
"multidepth": False,
|
||||
"depthperpass": 0.002,
|
||||
"extracut": False,
|
||||
"extracut_length": 0.1,
|
||||
"endz": 2.0,
|
||||
"startz": None,
|
||||
"toolchange": False,
|
||||
|
@ -3684,6 +3784,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
"feedrate_probe": self.ui.feedrate_probe_entry,
|
||||
"depthperpass": self.ui.maxdepth_entry,
|
||||
"extracut": self.ui.extracut_cb,
|
||||
"extracut_length": self.ui.e_cut_entry,
|
||||
"toolchange": self.ui.toolchangeg_cb,
|
||||
"toolchangez": self.ui.toolchangez_entry,
|
||||
"endz": self.ui.gendz_entry,
|
||||
|
@ -3722,10 +3823,11 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
"ppname_g": None,
|
||||
"depthperpass": None,
|
||||
"extracut": None,
|
||||
"extracut_length": None,
|
||||
"toolchange": None,
|
||||
"toolchangez": None,
|
||||
"endz": None,
|
||||
"spindlespeed": None,
|
||||
"spindlespeed": 0,
|
||||
"toolchangexy": None,
|
||||
"startz": None
|
||||
})
|
||||
|
@ -3814,6 +3916,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
self.ui.fr_rapidlabel.hide()
|
||||
self.ui.cncfeedrate_rapid_entry.hide()
|
||||
self.ui.extracut_cb.hide()
|
||||
self.ui.e_cut_entry.hide()
|
||||
self.ui.pdepth_label.hide()
|
||||
self.ui.pdepth_entry.hide()
|
||||
self.ui.feedrate_probe_label.hide()
|
||||
|
@ -3821,9 +3924,12 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
else:
|
||||
self.ui.level.setText('<span style="color:red;"><b>%s</b></span>' % _('Advanced'))
|
||||
|
||||
self.ui.e_cut_entry.setDisabled(True)
|
||||
|
||||
self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click)
|
||||
self.ui.generate_cnc_button.clicked.connect(self.on_generatecnc_button_click)
|
||||
self.ui.paint_tool_button.clicked.connect(lambda: self.app.paint_tool.run(toggle=False))
|
||||
self.ui.generate_ncc_button.clicked.connect(lambda: self.app.ncclear_tool.run(toggle=False))
|
||||
self.ui.pp_geometry_name_cb.activated.connect(self.on_pp_changed)
|
||||
self.ui.addtool_entry.returnPressed.connect(lambda: self.on_tool_add())
|
||||
|
||||
|
@ -4975,6 +5081,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
feedrate_rapid = tools_dict[tooluid_key]['data']["feedrate_rapid"]
|
||||
multidepth = tools_dict[tooluid_key]['data']["multidepth"]
|
||||
extracut = tools_dict[tooluid_key]['data']["extracut"]
|
||||
extracut_length = tools_dict[tooluid_key]['data']["extracut_length"]
|
||||
depthpercut = tools_dict[tooluid_key]['data']["depthperpass"]
|
||||
toolchange = tools_dict[tooluid_key]['data']["toolchange"]
|
||||
toolchangez = tools_dict[tooluid_key]['data']["toolchangez"]
|
||||
|
@ -5006,7 +5113,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
feedrate=feedrate, feedrate_z=feedrate_z, feedrate_rapid=feedrate_rapid,
|
||||
spindlespeed=spindlespeed, spindledir=spindledir, dwell=dwell, dwelltime=dwelltime,
|
||||
multidepth=multidepth, depthpercut=depthpercut,
|
||||
extracut=extracut, startz=startz, endz=endz,
|
||||
extracut=extracut, extracut_length=extracut_length, startz=startz, endz=endz,
|
||||
toolchange=toolchange, toolchangez=toolchangez, toolchangexy=toolchangexy,
|
||||
pp_geometry_name=pp_geometry_name,
|
||||
tool_no=tool_cnt)
|
||||
|
@ -5127,6 +5234,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
feedrate_rapid = tools_dict[tooluid_key]['data']["feedrate_rapid"]
|
||||
multidepth = tools_dict[tooluid_key]['data']["multidepth"]
|
||||
extracut = tools_dict[tooluid_key]['data']["extracut"]
|
||||
extracut_length = tools_dict[tooluid_key]['data']["extracut_length"]
|
||||
depthpercut = tools_dict[tooluid_key]['data']["depthperpass"]
|
||||
toolchange = tools_dict[tooluid_key]['data']["toolchange"]
|
||||
toolchangez = tools_dict[tooluid_key]['data']["toolchangez"]
|
||||
|
@ -5158,7 +5266,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
feedrate=feedrate, feedrate_z=feedrate_z, feedrate_rapid=feedrate_rapid,
|
||||
spindlespeed=spindlespeed, spindledir=spindledir, dwell=dwell, dwelltime=dwelltime,
|
||||
multidepth=multidepth, depthpercut=depthpercut,
|
||||
extracut=extracut, startz=startz, endz=endz,
|
||||
extracut=extracut, extracut_length=extracut_length, startz=startz, endz=endz,
|
||||
toolchange=toolchange, toolchangez=toolchangez, toolchangexy=toolchangexy,
|
||||
pp_geometry_name=pp_geometry_name,
|
||||
tool_no=tool_cnt)
|
||||
|
@ -5226,7 +5334,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
spindlespeed=None, dwell=None, dwelltime=None,
|
||||
multidepth=None, depthperpass=None,
|
||||
toolchange=None, toolchangez=None, toolchangexy=None,
|
||||
extracut=None, startz=None, endz=None,
|
||||
extracut=None, extracut_length=None, startz=None, endz=None,
|
||||
pp=None,
|
||||
segx=None, segy=None,
|
||||
use_thread=True,
|
||||
|
@ -5266,6 +5374,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
segy = segy if segy is not None else float(self.app.defaults['geometry_segy'])
|
||||
|
||||
extracut = extracut if extracut is not None else float(self.options["extracut"])
|
||||
extracut_length = extracut_length if extracut_length is not None else float(self.options["extracut_length"])
|
||||
|
||||
startz = startz if startz is not None else self.options["startz"]
|
||||
endz = endz if endz is not None else float(self.options["endz"])
|
||||
|
||||
|
@ -5320,7 +5430,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
spindlespeed=spindlespeed, dwell=dwell, dwelltime=dwelltime,
|
||||
multidepth=multidepth, depthpercut=depthperpass,
|
||||
toolchange=toolchange, toolchangez=toolchangez, toolchangexy=toolchangexy,
|
||||
extracut=extracut, startz=startz, endz=endz,
|
||||
extracut=extracut, extracut_length=extracut_length, startz=startz, endz=endz,
|
||||
pp_geometry_name=ppname_g
|
||||
)
|
||||
|
||||
|
@ -5625,7 +5735,6 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
tooldia = self.ui.addtool_entry.get_value()
|
||||
if tooldia:
|
||||
tooldia *= factor
|
||||
# limit the decimals to 2 for METRIC and 3 for INCH
|
||||
tooldia = float('%.*f' % (self.decimals, tooldia))
|
||||
|
||||
self.ui.addtool_entry.set_value(tooldia)
|
||||
|
@ -5635,7 +5744,6 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
def plot_element(self, element, color='#FF0000FF', visible=None):
|
||||
|
||||
visible = visible if visible else self.options['plot']
|
||||
|
||||
try:
|
||||
for sub_el in element:
|
||||
self.plot_element(sub_el)
|
||||
|
@ -5951,15 +6059,22 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
|
|||
self.ui_disconnect()
|
||||
|
||||
FlatCAMObj.build_ui(self)
|
||||
|
||||
# if the FlatCAM object is Excellon don't build the CNC Tools Table but hide it
|
||||
if self.cnc_tools:
|
||||
self.ui.cnc_tools_table.show()
|
||||
else:
|
||||
self.ui.cnc_tools_table.hide()
|
||||
|
||||
self.units = self.app.defaults['units'].upper()
|
||||
|
||||
# if the FlatCAM object is Excellon don't build the CNC Tools Table but hide it
|
||||
self.ui.cnc_tools_table.hide()
|
||||
if self.cnc_tools:
|
||||
self.ui.cnc_tools_table.show()
|
||||
self.build_cnc_tools_table()
|
||||
|
||||
self.ui.exc_cnc_tools_table.hide()
|
||||
if self.exc_cnc_tools:
|
||||
self.ui.exc_cnc_tools_table.show()
|
||||
self.build_excellon_cnc_tools()
|
||||
#
|
||||
self.ui_connect()
|
||||
|
||||
def build_cnc_tools_table(self):
|
||||
offset = 0
|
||||
tool_idx = 0
|
||||
|
||||
|
@ -6058,7 +6173,90 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
|
|||
self.ui.cnc_tools_table.setMinimumHeight(self.ui.cnc_tools_table.getHeight())
|
||||
self.ui.cnc_tools_table.setMaximumHeight(self.ui.cnc_tools_table.getHeight())
|
||||
|
||||
self.ui_connect()
|
||||
def build_excellon_cnc_tools(self):
|
||||
tool_idx = 0
|
||||
|
||||
n = len(self.exc_cnc_tools)
|
||||
self.ui.exc_cnc_tools_table.setRowCount(n)
|
||||
|
||||
for tooldia_key, dia_value in self.exc_cnc_tools.items():
|
||||
|
||||
tool_idx += 1
|
||||
row_no = tool_idx - 1
|
||||
|
||||
id = QtWidgets.QTableWidgetItem('%d' % int(tool_idx))
|
||||
dia_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, float(tooldia_key)))
|
||||
nr_drills_item = QtWidgets.QTableWidgetItem('%d' % int(dia_value['nr_drills']))
|
||||
nr_slots_item = QtWidgets.QTableWidgetItem('%d' % int(dia_value['nr_slots']))
|
||||
cutz_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, float(dia_value['offset_z']) + self.z_cut))
|
||||
|
||||
id.setFlags(QtCore.Qt.ItemIsEnabled)
|
||||
dia_item.setFlags(QtCore.Qt.ItemIsEnabled)
|
||||
nr_drills_item.setFlags(QtCore.Qt.ItemIsEnabled)
|
||||
nr_slots_item.setFlags(QtCore.Qt.ItemIsEnabled)
|
||||
cutz_item.setFlags(QtCore.Qt.ItemIsEnabled)
|
||||
|
||||
# hack so the checkbox stay centered in the table cell
|
||||
# used this:
|
||||
# https://stackoverflow.com/questions/32458111/pyqt-allign-checkbox-and-put-it-in-every-row
|
||||
# plot_item = QtWidgets.QWidget()
|
||||
# checkbox = FCCheckBox()
|
||||
# checkbox.setCheckState(QtCore.Qt.Checked)
|
||||
# qhboxlayout = QtWidgets.QHBoxLayout(plot_item)
|
||||
# qhboxlayout.addWidget(checkbox)
|
||||
# qhboxlayout.setAlignment(QtCore.Qt.AlignCenter)
|
||||
# qhboxlayout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
plot_item = FCCheckBox()
|
||||
plot_item.setLayoutDirection(QtCore.Qt.RightToLeft)
|
||||
tool_uid_item = QtWidgets.QTableWidgetItem(str(dia_value['tool']))
|
||||
if self.ui.plot_cb.isChecked():
|
||||
plot_item.setChecked(True)
|
||||
|
||||
# TODO until the feature of individual plot for an Excellon tool is implemented
|
||||
plot_item.setDisabled(True)
|
||||
|
||||
self.ui.exc_cnc_tools_table.setItem(row_no, 0, id) # Tool name/id
|
||||
self.ui.exc_cnc_tools_table.setItem(row_no, 1, dia_item) # Diameter
|
||||
self.ui.exc_cnc_tools_table.setItem(row_no, 2, nr_drills_item) # Nr of drills
|
||||
self.ui.exc_cnc_tools_table.setItem(row_no, 3, nr_slots_item) # Nr of slots
|
||||
|
||||
# ## REMEMBER: THIS COLUMN IS HIDDEN IN OBJECTUI.PY # ##
|
||||
self.ui.exc_cnc_tools_table.setItem(row_no, 4, tool_uid_item) # Tool unique ID)
|
||||
self.ui.exc_cnc_tools_table.setItem(row_no, 5, cutz_item)
|
||||
self.ui.exc_cnc_tools_table.setCellWidget(row_no, 6, plot_item)
|
||||
|
||||
for row in range(tool_idx):
|
||||
self.ui.exc_cnc_tools_table.item(row, 0).setFlags(
|
||||
self.ui.exc_cnc_tools_table.item(row, 0).flags() ^ QtCore.Qt.ItemIsSelectable)
|
||||
|
||||
self.ui.exc_cnc_tools_table.resizeColumnsToContents()
|
||||
self.ui.exc_cnc_tools_table.resizeRowsToContents()
|
||||
|
||||
vertical_header = self.ui.exc_cnc_tools_table.verticalHeader()
|
||||
vertical_header.hide()
|
||||
self.ui.exc_cnc_tools_table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||
|
||||
horizontal_header = self.ui.exc_cnc_tools_table.horizontalHeader()
|
||||
horizontal_header.setMinimumSectionSize(10)
|
||||
horizontal_header.setDefaultSectionSize(70)
|
||||
horizontal_header.setSectionResizeMode(0, QtWidgets.QHeaderView.Fixed)
|
||||
horizontal_header.resizeSection(0, 20)
|
||||
horizontal_header.setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch)
|
||||
horizontal_header.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents)
|
||||
horizontal_header.setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeToContents)
|
||||
horizontal_header.setSectionResizeMode(5, QtWidgets.QHeaderView.ResizeToContents)
|
||||
|
||||
horizontal_header.setSectionResizeMode(6, QtWidgets.QHeaderView.Fixed)
|
||||
|
||||
# horizontal_header.setStretchLastSection(True)
|
||||
self.ui.exc_cnc_tools_table.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||
|
||||
self.ui.exc_cnc_tools_table.setColumnWidth(0, 20)
|
||||
self.ui.exc_cnc_tools_table.setColumnWidth(6, 17)
|
||||
|
||||
self.ui.exc_cnc_tools_table.setMinimumHeight(self.ui.exc_cnc_tools_table.getHeight())
|
||||
self.ui.exc_cnc_tools_table.setMaximumHeight(self.ui.exc_cnc_tools_table.getHeight())
|
||||
|
||||
def set_ui(self, ui):
|
||||
FlatCAMObj.set_ui(self, ui)
|
||||
|
@ -6645,10 +6843,20 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
|
|||
self.plot2(dia_plot, obj=self, visible=visible, kind=kind)
|
||||
else:
|
||||
# multiple tools usage
|
||||
for tooluid_key in self.cnc_tools:
|
||||
tooldia = float('%.*f' % (self.decimals, float(self.cnc_tools[tooluid_key]['tooldia'])))
|
||||
gcode_parsed = self.cnc_tools[tooluid_key]['gcode_parsed']
|
||||
self.plot2(tooldia=tooldia, obj=self, visible=visible, gcode_parsed=gcode_parsed, kind=kind)
|
||||
if self.cnc_tools:
|
||||
for tooluid_key in self.cnc_tools:
|
||||
tooldia = float('%.*f' % (self.decimals, float(self.cnc_tools[tooluid_key]['tooldia'])))
|
||||
gcode_parsed = self.cnc_tools[tooluid_key]['gcode_parsed']
|
||||
self.plot2(tooldia=tooldia, obj=self, visible=visible, gcode_parsed=gcode_parsed, kind=kind)
|
||||
|
||||
# TODO: until the gcode parsed will be stored on each Excellon tool this will not get executed
|
||||
if self.exc_cnc_tools:
|
||||
for tooldia_key in self.exc_cnc_tools:
|
||||
tooldia = float('%.*f' % (self.decimals, float(tooldia_key)))
|
||||
# gcode_parsed = self.cnc_tools[tooldia_key]['gcode_parsed']
|
||||
gcode_parsed = self.gcode_parsed
|
||||
self.plot2(tooldia=tooldia, obj=self, visible=visible, gcode_parsed=gcode_parsed, kind=kind)
|
||||
|
||||
self.shapes.redraw()
|
||||
except (ObjectDeleted, AttributeError):
|
||||
self.shapes.clear(update=True)
|
||||
|
|
|
@ -785,23 +785,41 @@ class ObjectCollection(QtCore.QAbstractItemModel):
|
|||
self.item_selected.emit(obj.options['name'])
|
||||
|
||||
if obj.kind == 'gerber':
|
||||
self.app.inform.emit(_('[selected]<span style="color:{color};">{name}</span> selected').format(
|
||||
color='green', name=str(obj.options['name'])))
|
||||
self.app.inform.emit('[selected]<span style="color:{color};">{name}</span> {tx}'.format(
|
||||
color='green',
|
||||
name=str(obj.options['name']),
|
||||
tx=_("selected"))
|
||||
)
|
||||
elif obj.kind == 'excellon':
|
||||
self.app.inform.emit(_('[selected]<span style="color:{color};">{name}</span> selected').format(
|
||||
color='brown', name=str(obj.options['name'])))
|
||||
self.app.inform.emit('[selected]<span style="color:{color};">{name}</span> {tx}'.format(
|
||||
color='brown',
|
||||
name=str(obj.options['name']),
|
||||
tx=_("selected"))
|
||||
)
|
||||
elif obj.kind == 'cncjob':
|
||||
self.app.inform.emit(_('[selected]<span style="color:{color};">{name}</span> selected').format(
|
||||
color='blue', name=str(obj.options['name'])))
|
||||
self.app.inform.emit('[selected]<span style="color:{color};">{name}</span> {tx}'.format(
|
||||
color='blue',
|
||||
name=str(obj.options['name']),
|
||||
tx=_("selected"))
|
||||
)
|
||||
elif obj.kind == 'geometry':
|
||||
self.app.inform.emit(_('[selected]<span style="color:{color};">{name}</span> selected').format(
|
||||
color='red', name=str(obj.options['name'])))
|
||||
self.app.inform.emit('[selected]<span style="color:{color};">{name}</span> {tx}'.format(
|
||||
color='red',
|
||||
name=str(obj.options['name']),
|
||||
tx=_("selected"))
|
||||
)
|
||||
elif obj.kind == 'script':
|
||||
self.app.inform.emit(_('[selected]<span style="color:{color};">{name}</span> selected').format(
|
||||
color='orange', name=str(obj.options['name'])))
|
||||
self.app.inform.emit('[selected]<span style="color:{color};">{name}</span> {tx}'.format(
|
||||
color='orange',
|
||||
name=str(obj.options['name']),
|
||||
tx=_("selected"))
|
||||
)
|
||||
elif obj.kind == 'document':
|
||||
self.app.inform.emit(_('[selected]<span style="color:{color};">{name}</span> selected').format(
|
||||
color='darkCyan', name=str(obj.options['name'])))
|
||||
self.app.inform.emit('[selected]<span style="color:{color};">{name}</span> {tx}'.format(
|
||||
color='darkCyan',
|
||||
name=str(obj.options['name']),
|
||||
tx=_("selected"))
|
||||
)
|
||||
except IndexError:
|
||||
self.item_selected.emit('none')
|
||||
# FlatCAMApp.App.log.debug("on_list_selection_change(): Index Error (Nothing selected?)")
|
||||
|
|
46
README.md
46
README.md
|
@ -9,19 +9,63 @@ CAD program, and create G-Code for Isolation routing.
|
|||
|
||||
=================================================
|
||||
|
||||
14.12.2019
|
||||
|
||||
- finished the strings update in the Google-translated Spanish
|
||||
|
||||
13.12.2019
|
||||
|
||||
- HPGL2 import: added support for circles, arcs and 3-point arcs. Everything works only for absolute coordinates.
|
||||
- removed the .plt extension from Gcode extensions
|
||||
- some strings updated; update on the Romanian translate
|
||||
- more strings updated; finished the Romanian translation update
|
||||
- some work in updating the Spanish Google-translation
|
||||
- small updates (Google Translate) in Russian and Brazilian-PT languages
|
||||
|
||||
12.12.2019
|
||||
|
||||
- finished the Calibration Tool
|
||||
- changed the Scale Entry in Object UI to FCEntry() GUI element in order to allow expressions to be entered. E.g: 1/25.4
|
||||
- some small changes in the Scale button handler in FlatCAMObj() class
|
||||
- added option to save objects as PDF files in File -> Save menu
|
||||
- optimized the FlatCAMGerber.clear_plot_apertures() method
|
||||
- some changes in the ObjectUI and for the Geometry UI
|
||||
- finished a very rough and limited HPGL2 file import
|
||||
|
||||
11.12.2019
|
||||
|
||||
- started work in HPGL2 parser
|
||||
- some more work in Calibration Tool
|
||||
|
||||
10.12.2019
|
||||
|
||||
- small changes in the Geometry UI
|
||||
- now extracut option in the Geometry Object will recut as many points as many they are within the specified re-cut length
|
||||
- if extracut_length is zero then the extracut will cut up until the first point in path no matter what the distance is
|
||||
- in Gerber isolation, when selection mode is checked, now area selection works too
|
||||
- in CNCJob UI, now the CNCJob objects made out of Excellon objects will display their CNC tools (drill bits)
|
||||
- fixed a cumulative error when using the Tool Offset for Excellon objects
|
||||
- added the display of the real depth of cut (cut z + offset_z) for CNC tools made out of an Excellon object
|
||||
- for OpenGL graphic mode added a fit_view() execution on canvas initialization
|
||||
- fixed Excellon scaling the UI values
|
||||
- replaced the SpindleSpeed entry with a FCSpinner() GUI element; if speed is set to 0 it will amount to None
|
||||
|
||||
9.12.2019
|
||||
|
||||
- updated the border for fit view on OpenGL graphic mode
|
||||
- Calibration Tool - added preferences values
|
||||
- Calibration Tool - more work on it
|
||||
- reverted this change: "selected object in Project used to ask twice for UI build" because it will not build the UI when a tab is closed for Document object and the object is selected
|
||||
- fixed issue after Geometry object edit; the GCode made from and edited object did not reflect the changes in the object
|
||||
- fixed issue after Geometry object edit; the GCode made from an edited object did not reflect the changes in the object
|
||||
- in Object UI, the Scale FCDoubleSpinner will no longer work for Return key press due of issues of unwanted scaling on focusOut event
|
||||
- in FlatCAMGeometry fixed the scale and offset methods to always process the self.solid_geometry
|
||||
- Calibration Tool - finished the calibrated object creation method
|
||||
- updated the POT file
|
||||
- fixed an error in the German PO file
|
||||
- updated the languages PO files
|
||||
- some fixes on the app.jump_to() method
|
||||
- made sure that the ToolFilm will not start saving a file if there are no objects loaded
|
||||
- some fixes on the app.jump_to() method for the Legacy(2D) graphic mode
|
||||
|
||||
8.12.2019
|
||||
|
||||
|
|
105
camlib.py
105
camlib.py
|
@ -459,7 +459,7 @@ class Geometry(object):
|
|||
|
||||
defaults = {
|
||||
"units": 'in',
|
||||
"geo_steps_per_circle": 128
|
||||
"geo_steps_per_circle": 64
|
||||
}
|
||||
|
||||
def __init__(self, geo_steps_per_circle=None):
|
||||
|
@ -1824,7 +1824,7 @@ class Geometry(object):
|
|||
"""
|
||||
|
||||
# Make sure we see a Shapely Geometry class and not a list
|
||||
if str(type(self)) == "<class 'FlatCAMObj.FlatCAMGeometry'>":
|
||||
if self.kind.lower() == 'geometry':
|
||||
flat_geo = []
|
||||
if self.multigeo:
|
||||
for tool in self.tools:
|
||||
|
@ -2158,7 +2158,7 @@ class CNCjob(Geometry):
|
|||
self.units = units
|
||||
|
||||
self.z_cut = z_cut
|
||||
self.tool_offset = {}
|
||||
self.tool_offset = dict()
|
||||
|
||||
self.z_move = z_move
|
||||
|
||||
|
@ -2359,7 +2359,9 @@ class CNCjob(Geometry):
|
|||
self.exc_drills = deepcopy(exobj.drills)
|
||||
self.exc_tools = deepcopy(exobj.tools)
|
||||
|
||||
self.z_cut = drillz
|
||||
self.z_cut = deepcopy(drillz)
|
||||
old_zcut = deepcopy(drillz)
|
||||
|
||||
if self.machinist_setting == 0:
|
||||
if drillz > 0:
|
||||
self.app.inform.emit('[WARNING] %s' %
|
||||
|
@ -2441,10 +2443,16 @@ class CNCjob(Geometry):
|
|||
LineString([start, stop]).buffer((it[1] / 2.0), resolution=self.geo_steps_per_circle)
|
||||
)
|
||||
|
||||
try:
|
||||
z_off = float(self.tool_offset[it[1]]) * (-1)
|
||||
except KeyError:
|
||||
z_off = 0
|
||||
|
||||
self.exc_cnc_tools[it[1]] = dict()
|
||||
self.exc_cnc_tools[it[1]]['tool'] = it[0]
|
||||
self.exc_cnc_tools[it[1]]['nr_drills'] = drill_no
|
||||
self.exc_cnc_tools[it[1]]['nr_slots'] = slot_no
|
||||
self.exc_cnc_tools[it[1]]['offset_z'] = z_off
|
||||
self.exc_cnc_tools[it[1]]['solid_geometry'] = deepcopy(sol_geo)
|
||||
|
||||
self.app.inform.emit(_("Creating a list of points to drill..."))
|
||||
|
@ -2635,7 +2643,7 @@ class CNCjob(Geometry):
|
|||
z_offset = float(self.tool_offset[current_tooldia]) * (-1)
|
||||
except KeyError:
|
||||
z_offset = 0
|
||||
self.z_cut += z_offset
|
||||
self.z_cut = z_offset + old_zcut
|
||||
|
||||
self.coordinates_type = self.app.defaults["cncjob_coords_type"]
|
||||
if self.coordinates_type == "G90":
|
||||
|
@ -2682,11 +2690,11 @@ class CNCjob(Geometry):
|
|||
else:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s...' % _('G91 coordinates not implemented'))
|
||||
return 'fail'
|
||||
self.z_cut = deepcopy(old_zcut)
|
||||
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'
|
||||
|
||||
log.debug("The total travel distance with OR-TOOLS Metaheuristics is: %s" % str(measured_distance))
|
||||
|
@ -2778,7 +2786,7 @@ class CNCjob(Geometry):
|
|||
z_offset = float(self.tool_offset[current_tooldia]) * (-1)
|
||||
except KeyError:
|
||||
z_offset = 0
|
||||
self.z_cut += z_offset
|
||||
self.z_cut = z_offset + old_zcut
|
||||
|
||||
self.coordinates_type = self.app.defaults["cncjob_coords_type"]
|
||||
if self.coordinates_type == "G90":
|
||||
|
@ -2825,6 +2833,7 @@ class CNCjob(Geometry):
|
|||
else:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s...' % _('G91 coordinates not implemented'))
|
||||
return 'fail'
|
||||
self.z_cut = deepcopy(old_zcut)
|
||||
else:
|
||||
log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
|
||||
"The loaded Excellon file has no drills ...")
|
||||
|
@ -2879,7 +2888,7 @@ class CNCjob(Geometry):
|
|||
z_offset = float(self.tool_offset[current_tooldia]) * (-1)
|
||||
except KeyError:
|
||||
z_offset = 0
|
||||
self.z_cut += z_offset
|
||||
self.z_cut = z_offset + old_zcut
|
||||
|
||||
self.coordinates_type = self.app.defaults["cncjob_coords_type"]
|
||||
if self.coordinates_type == "G90":
|
||||
|
@ -2933,6 +2942,7 @@ class CNCjob(Geometry):
|
|||
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))
|
||||
|
||||
gcode += self.doformat(p.spindle_stop_code) # Spindle stop
|
||||
|
@ -2962,7 +2972,7 @@ class CNCjob(Geometry):
|
|||
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,
|
||||
toolchange=False, toolchangez=1.0, toolchangexy="0.0, 0.0", extracut=False, extracut_length=0.2,
|
||||
startz=None, endz=2.0, pp_geometry_name=None, tool_no=1):
|
||||
"""
|
||||
Algorithm to generate from multitool Geometry.
|
||||
|
@ -2992,6 +3002,7 @@ class CNCjob(Geometry):
|
|||
:param toolchangexy:
|
||||
:param extracut: Adds (or not) an extra cut at the end of each path overlapping the
|
||||
first point in path to ensure complete copper removal
|
||||
:param extracut_length: Extra cut legth at the end of the path
|
||||
:param startz:
|
||||
:param endz:
|
||||
:param pp_geometry_name:
|
||||
|
@ -3025,7 +3036,7 @@ class CNCjob(Geometry):
|
|||
self.z_feedrate = float(feedrate_z) if feedrate_z is not None else None
|
||||
self.feedrate_rapid = float(feedrate_rapid) if feedrate_rapid else None
|
||||
|
||||
self.spindlespeed = int(spindlespeed) if spindlespeed else None
|
||||
self.spindlespeed = int(spindlespeed) if spindlespeed != 0 else None
|
||||
self.spindledir = spindledir
|
||||
self.dwell = dwell
|
||||
self.dwelltime = float(dwelltime) if dwelltime else None
|
||||
|
@ -3213,7 +3224,8 @@ class CNCjob(Geometry):
|
|||
# calculate the cut distance
|
||||
total_cut = total_cut + geo.length
|
||||
|
||||
self.gcode += self.create_gcode_single_pass(geo, extracut, tolerance, old_point=current_pt)
|
||||
self.gcode += self.create_gcode_single_pass(geo, extracut, extracut_length, tolerance,
|
||||
old_point=current_pt)
|
||||
|
||||
# --------- Multi-pass ---------
|
||||
else:
|
||||
|
@ -3227,7 +3239,7 @@ class CNCjob(Geometry):
|
|||
|
||||
total_cut += (geo.length * nr_cuts)
|
||||
|
||||
self.gcode += self.create_gcode_multi_pass(geo, extracut, tolerance,
|
||||
self.gcode += self.create_gcode_multi_pass(geo, extracut, extracut_length, tolerance,
|
||||
postproc=p, old_point=current_pt)
|
||||
|
||||
# calculate the total distance
|
||||
|
@ -3270,7 +3282,7 @@ class CNCjob(Geometry):
|
|||
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, startz=None, endz=2.0,
|
||||
extracut=False, extracut_length=0.1, startz=None, endz=2.0,
|
||||
pp_geometry_name=None, tool_no=1):
|
||||
"""
|
||||
Second algorithm to generate from Geometry.
|
||||
|
@ -3288,6 +3300,7 @@ class CNCjob(Geometry):
|
|||
:param depthpercut: Maximum depth in each pass.
|
||||
:param extracut: Adds (or not) an extra cut at the end of each path
|
||||
overlapping the first point in path to ensure complete copper removal
|
||||
:param extracut_length: The extra cut length
|
||||
:return: None
|
||||
"""
|
||||
|
||||
|
@ -3375,7 +3388,7 @@ class CNCjob(Geometry):
|
|||
self.z_feedrate = float(feedrate_z) if feedrate_z is not None else None
|
||||
self.feedrate_rapid = float(feedrate_rapid) if feedrate_rapid else None
|
||||
|
||||
self.spindlespeed = int(spindlespeed) if spindlespeed else None
|
||||
self.spindlespeed = int(spindlespeed) if spindlespeed != 0 else None
|
||||
self.spindledir = spindledir
|
||||
self.dwell = dwell
|
||||
self.dwelltime = float(dwelltime) if dwelltime else None
|
||||
|
@ -3559,7 +3572,8 @@ class CNCjob(Geometry):
|
|||
if not multidepth:
|
||||
# calculate the cut distance
|
||||
total_cut += geo.length
|
||||
self.gcode += self.create_gcode_single_pass(geo, extracut, tolerance, old_point=current_pt)
|
||||
self.gcode += self.create_gcode_single_pass(geo, extracut, extracut_length, tolerance,
|
||||
old_point=current_pt)
|
||||
|
||||
# --------- Multi-pass ---------
|
||||
else:
|
||||
|
@ -3573,7 +3587,7 @@ class CNCjob(Geometry):
|
|||
|
||||
total_cut += (geo.length * nr_cuts)
|
||||
|
||||
self.gcode += self.create_gcode_multi_pass(geo, extracut, tolerance,
|
||||
self.gcode += self.create_gcode_multi_pass(geo, extracut, extracut_length, tolerance,
|
||||
postproc=p, old_point=current_pt)
|
||||
|
||||
# calculate the travel distance
|
||||
|
@ -3798,7 +3812,7 @@ class CNCjob(Geometry):
|
|||
gcode += self.doformat(p.lift_code)
|
||||
return gcode
|
||||
|
||||
def create_gcode_single_pass(self, geometry, extracut, tolerance, old_point=(0, 0)):
|
||||
def create_gcode_single_pass(self, geometry, extracut, extracut_length, tolerance, old_point=(0, 0)):
|
||||
# G-code. Note: self.linear2gcode() and self.point2gcode() will lower and raise the tool every time.
|
||||
gcode_single_pass = ''
|
||||
|
||||
|
@ -3807,7 +3821,8 @@ class CNCjob(Geometry):
|
|||
gcode_single_pass = self.linear2gcode(geometry, tolerance=tolerance, old_point=old_point)
|
||||
else:
|
||||
if geometry.is_ring:
|
||||
gcode_single_pass = self.linear2gcode_extra(geometry, tolerance=tolerance, old_point=old_point)
|
||||
gcode_single_pass = self.linear2gcode_extra(geometry, extracut_length, tolerance=tolerance,
|
||||
old_point=old_point)
|
||||
else:
|
||||
gcode_single_pass = self.linear2gcode(geometry, tolerance=tolerance, old_point=old_point)
|
||||
elif type(geometry) == Point:
|
||||
|
@ -3818,7 +3833,7 @@ class CNCjob(Geometry):
|
|||
|
||||
return gcode_single_pass
|
||||
|
||||
def create_gcode_multi_pass(self, geometry, extracut, tolerance, postproc, old_point=(0, 0)):
|
||||
def create_gcode_multi_pass(self, geometry, extracut, extracut_length, tolerance, postproc, old_point=(0, 0)):
|
||||
|
||||
gcode_multi_pass = ''
|
||||
|
||||
|
@ -3851,8 +3866,8 @@ class CNCjob(Geometry):
|
|||
old_point=old_point)
|
||||
else:
|
||||
if geometry.is_ring:
|
||||
gcode_multi_pass += self.linear2gcode_extra(geometry, tolerance=tolerance, z_cut=depth,
|
||||
up=False, old_point=old_point)
|
||||
gcode_multi_pass += self.linear2gcode_extra(geometry, extracut_length, tolerance=tolerance,
|
||||
z_cut=depth, up=False, old_point=old_point)
|
||||
else:
|
||||
gcode_multi_pass += self.linear2gcode(geometry, tolerance=tolerance, z_cut=depth, up=False,
|
||||
old_point=old_point)
|
||||
|
@ -4513,13 +4528,14 @@ class CNCjob(Geometry):
|
|||
gcode += self.doformat(p.lift_code, x=prev_x, y=prev_y, z_move=z_move) # Stop cutting
|
||||
return gcode
|
||||
|
||||
def linear2gcode_extra(self, linear, tolerance=0, down=True, up=True,
|
||||
def linear2gcode_extra(self, linear, extracut_length, 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)):
|
||||
"""
|
||||
Generates G-code to cut along the linear feature.
|
||||
|
||||
:param linear: The path to cut along.
|
||||
:param extracut_length: how much to cut extra over the first point at the end of the path
|
||||
:type: Shapely.LinearRing or Shapely.Linear String
|
||||
:param tolerance: All points in the simplified object will be within the
|
||||
tolerance distance of the original geometry.
|
||||
|
@ -4602,8 +4618,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]
|
||||
|
||||
|
@ -4614,19 +4629,41 @@ class CNCjob(Geometry):
|
|||
# this line is added to create an extra cut over the first point in patch
|
||||
# to make sure that we remove the copper leftovers
|
||||
# Linear motion to the 1st point in the cut path
|
||||
if self.coordinates_type == "G90":
|
||||
# For Absolute coordinates type G90
|
||||
last_x = path[1][0]
|
||||
last_y = path[1][1]
|
||||
# if self.coordinates_type == "G90":
|
||||
# # For Absolute coordinates type G90
|
||||
# last_x = path[1][0]
|
||||
# last_y = path[1][1]
|
||||
# else:
|
||||
# # For Incremental coordinates type G91
|
||||
# last_x = path[1][0] - first_x
|
||||
# last_y = path[1][1] - first_y
|
||||
# gcode += self.doformat(p.linear_code, x=last_x, y=last_y)
|
||||
|
||||
# the first point for extracut is always mandatory if the extracut is enabled. But if the length of distance
|
||||
# between point 0 and point 1 is more than the distance we set for the extra cut then make an interpolation
|
||||
# along the path and find the point at the distance extracut_length
|
||||
|
||||
if extracut_length == 0.0:
|
||||
gcode += self.doformat(p.linear_code, x=path[1][0], y=path[1][1])
|
||||
last_pt = path[1]
|
||||
else:
|
||||
# For Incremental coordinates type G91
|
||||
last_x = path[1][0] - first_x
|
||||
last_y = path[1][1] - first_y
|
||||
gcode += self.doformat(p.linear_code, x=last_x, y=last_y)
|
||||
if abs(distance(path[1], path[0])) > extracut_length:
|
||||
i_point = LineString([path[0], path[1]]).interpolate(extracut_length)
|
||||
gcode += self.doformat(p.linear_code, x=i_point.x, y=i_point.y)
|
||||
last_pt = (i_point.x, i_point.y)
|
||||
else:
|
||||
last_pt = path[0]
|
||||
for pt in path[1:]:
|
||||
extracut_distance = abs(distance(pt, last_pt))
|
||||
if extracut_distance <= extracut_length:
|
||||
gcode += self.doformat(p.linear_code, x=pt[0], y=pt[1])
|
||||
last_pt = pt
|
||||
else:
|
||||
break
|
||||
|
||||
# Up to travelling height.
|
||||
if up:
|
||||
gcode += self.doformat(p.lift_code, x=last_x, y=last_y, z_move=z_move) # Stop cutting
|
||||
gcode += self.doformat(p.lift_code, x=last_pt[0], y=last_pt[1], z_move=z_move) # Stop cutting
|
||||
|
||||
return gcode
|
||||
|
||||
|
|
|
@ -455,8 +455,6 @@ class PaintOptionsTool(FlatCAMTool):
|
|||
ovlabel = QtWidgets.QLabel('%s:' % _('Overlap Rate'))
|
||||
ovlabel.setToolTip(
|
||||
_("How much (fraction) of the tool width to overlap each tool pass.\n"
|
||||
"Example:\n"
|
||||
"A value here of 0.25 means 25%% from the tool diameter found above.\n\n"
|
||||
"Adjust the value starting with lower values\n"
|
||||
"and increasing it if areas that should be painted are still \n"
|
||||
"not painted.\n"
|
||||
|
|
|
@ -168,6 +168,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
_('&DXF as Gerber Object ...'), self)
|
||||
self.menufileimport.addAction(self.menufileimportdxf_as_gerber)
|
||||
self.menufileimport.addSeparator()
|
||||
self.menufileimport_hpgl2_as_geo = QtWidgets.QAction(QtGui.QIcon('share/dxf16.png'),
|
||||
_('HPGL2 as Geometry Object ...'), self)
|
||||
self.menufileimport.addAction(self.menufileimport_hpgl2_as_geo)
|
||||
self.menufileimport.addSeparator()
|
||||
|
||||
# Export ...
|
||||
self.menufileexport = self.menufile.addMenu(QtGui.QIcon('share/export.png'), _('Export'))
|
||||
|
@ -250,6 +254,13 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
_('Save Project C&opy ...'), self)
|
||||
self.menufile_save.addAction(self.menufilesaveprojectcopy)
|
||||
|
||||
self.menufile_save.addSeparator()
|
||||
|
||||
# Save Object PDF
|
||||
self.menufilesave_object_pdf = QtWidgets.QAction(QtGui.QIcon('share/pdf32.png'),
|
||||
_('Save Object as PDF ...'), self)
|
||||
self.menufile_save.addAction(self.menufilesave_object_pdf)
|
||||
|
||||
# Separator
|
||||
self.menufile.addSeparator()
|
||||
|
||||
|
@ -754,6 +765,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
_("Copper Thieving Tool"))
|
||||
|
||||
self.fiducials_btn = self.toolbartools.addAction(QtGui.QIcon('share/fiducials_32.png'), _("Fiducials Tool"))
|
||||
self.cal_btn = self.toolbartools.addAction(QtGui.QIcon('share/calibrate_32.png'), _("Calibration Tool"))
|
||||
|
||||
# ########################################################################
|
||||
# ########################## Excellon Editor Toolbar# ####################
|
||||
|
@ -2198,6 +2210,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
_("Copper Thieving Tool"))
|
||||
|
||||
self.fiducials_btn = self.toolbartools.addAction(QtGui.QIcon('share/fiducials_32.png'), _("Fiducials Tool"))
|
||||
self.cal_btn = self.toolbartools.addAction(QtGui.QIcon('share/calibrate_32.png'), _("Calibration Tool"))
|
||||
|
||||
# ## Excellon Editor Toolbar # ##
|
||||
self.select_drill_btn = self.exc_edit_toolbar.addAction(QtGui.QIcon('share/pointer32.png'), _("Select"))
|
||||
|
@ -2530,7 +2543,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.app.dblsidedtool.run(toggle=True)
|
||||
return
|
||||
|
||||
# Calibrate Tool
|
||||
# Calibration Tool
|
||||
if key == QtCore.Qt.Key_E:
|
||||
self.app.cal_exc_tool.run(toggle=True)
|
||||
return
|
||||
|
|
|
@ -100,13 +100,10 @@ class ObjectUI(QtWidgets.QWidget):
|
|||
faclabel = QtWidgets.QLabel('%s:' % _('Factor'))
|
||||
faclabel.setToolTip(
|
||||
_("Factor by which to multiply\n"
|
||||
"geometric features of this object.")
|
||||
"geometric features of this object.\n"
|
||||
"Expressions are allowed. E.g: 1/25.4")
|
||||
)
|
||||
self.scale_entry = FCDoubleSpinner()
|
||||
self.scale_entry.set_precision(self.decimals)
|
||||
self.scale_entry.setRange(0.0, 9999.9999)
|
||||
self.scale_entry.setSingleStep(0.1)
|
||||
|
||||
self.scale_entry = FCEntry()
|
||||
self.scale_entry.set_value(1.0)
|
||||
|
||||
# GO Button
|
||||
|
@ -131,7 +128,8 @@ class ObjectUI(QtWidgets.QWidget):
|
|||
self.offset_vectorlabel = QtWidgets.QLabel('%s:' % _('Vector'))
|
||||
self.offset_vectorlabel.setToolTip(
|
||||
_("Amount by which to move the object\n"
|
||||
"in the x and y axes in (x, y) format.")
|
||||
"in the x and y axes in (x, y) format.\n"
|
||||
"Expressions are allowed. E.g: (1/3.2, 0.5*3)")
|
||||
)
|
||||
self.offsetvector_entry = EvalEntry2()
|
||||
self.offsetvector_entry.setText("(0.0, 0.0)")
|
||||
|
@ -158,6 +156,8 @@ class GerberObjectUI(ObjectUI):
|
|||
ObjectUI.__init__(self, title=_('Gerber Object'), parent=parent, decimals=decimals)
|
||||
self.decimals = decimals
|
||||
|
||||
self.custom_box.addWidget(QtWidgets.QLabel(''))
|
||||
|
||||
# Plot options
|
||||
grid0 = QtWidgets.QGridLayout()
|
||||
grid0.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
|
||||
|
@ -165,10 +165,20 @@ class GerberObjectUI(ObjectUI):
|
|||
grid0.setColumnStretch(0, 0)
|
||||
grid0.setColumnStretch(1, 1)
|
||||
|
||||
# Plot CB
|
||||
self.plot_cb = FCCheckBox()
|
||||
self.plot_cb.setToolTip(
|
||||
_("Plot (show) this object.")
|
||||
)
|
||||
plot_label = QtWidgets.QLabel('<b>%s:</b>' % _("Plot"))
|
||||
|
||||
grid0.addWidget(plot_label, 0, 0)
|
||||
grid0.addWidget(self.plot_cb, 0, 1)
|
||||
|
||||
self.plot_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("Plot Options"))
|
||||
self.plot_options_label.setMinimumWidth(90)
|
||||
|
||||
grid0.addWidget(self.plot_options_label, 0, 0)
|
||||
grid0.addWidget(self.plot_options_label, 1, 0)
|
||||
|
||||
# Solid CB
|
||||
self.solid_cb = FCCheckBox(label=_('Solid'))
|
||||
|
@ -176,23 +186,15 @@ class GerberObjectUI(ObjectUI):
|
|||
_("Solid color polygons.")
|
||||
)
|
||||
self.solid_cb.setMinimumWidth(50)
|
||||
grid0.addWidget(self.solid_cb, 0, 1)
|
||||
grid0.addWidget(self.solid_cb, 1, 1)
|
||||
|
||||
# Multicolored CB
|
||||
self.multicolored_cb = FCCheckBox(label=_('M-Color'))
|
||||
self.multicolored_cb = FCCheckBox(label=_('Multi-Color'))
|
||||
self.multicolored_cb.setToolTip(
|
||||
_("Draw polygons in different colors.")
|
||||
)
|
||||
self.multicolored_cb.setMinimumWidth(55)
|
||||
grid0.addWidget(self.multicolored_cb, 0, 2)
|
||||
|
||||
# Plot CB
|
||||
self.plot_cb = FCCheckBox(_('Plot'))
|
||||
self.plot_cb.setToolTip(
|
||||
_("Plot (show) this object.")
|
||||
)
|
||||
self.plot_cb.setMinimumWidth(59)
|
||||
grid0.addWidget(self.plot_cb, 0, 3)
|
||||
grid0.addWidget(self.multicolored_cb, 1, 2)
|
||||
|
||||
# ## Object name
|
||||
self.name_hlay = QtWidgets.QHBoxLayout()
|
||||
|
@ -264,7 +266,10 @@ class GerberObjectUI(ObjectUI):
|
|||
# start with apertures table hidden
|
||||
self.apertures_table.setVisible(False)
|
||||
|
||||
self.custom_box.addWidget(QtWidgets.QLabel(''))
|
||||
separator_line = QtWidgets.QFrame()
|
||||
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
self.custom_box.addWidget(separator_line)
|
||||
|
||||
# Isolation Routing
|
||||
self.isolation_routing_label = QtWidgets.QLabel("<b>%s</b>" % _("Isolation Routing"))
|
||||
|
@ -552,6 +557,12 @@ class GerberObjectUI(ObjectUI):
|
|||
_("Create the Geometry Object\n"
|
||||
"for non-copper routing.")
|
||||
)
|
||||
self.generate_ncc_button.setStyleSheet("""
|
||||
QPushButton
|
||||
{
|
||||
font-weight: bold;
|
||||
}
|
||||
""")
|
||||
grid2.addWidget(self.clearcopper_label, 1, 0)
|
||||
grid2.addWidget(self.generate_ncc_button, 1, 1)
|
||||
|
||||
|
@ -568,6 +579,12 @@ class GerberObjectUI(ObjectUI):
|
|||
_("Generate the geometry for\n"
|
||||
"the board cutout.")
|
||||
)
|
||||
self.generate_cutout_button.setStyleSheet("""
|
||||
QPushButton
|
||||
{
|
||||
font-weight: bold;
|
||||
}
|
||||
""")
|
||||
grid2.addWidget(self.board_cutout_label, 2, 0)
|
||||
grid2.addWidget(self.generate_cutout_button, 2, 1)
|
||||
|
||||
|
@ -913,7 +930,9 @@ class ExcellonObjectUI(ObjectUI):
|
|||
"in RPM (optional)")
|
||||
)
|
||||
grid1.addWidget(spdlabel, 8, 0)
|
||||
self.spindlespeed_entry = IntEntry(allow_empty=True)
|
||||
self.spindlespeed_entry = FCSpinner()
|
||||
self.spindlespeed_entry.set_range(0, 1000000)
|
||||
self.spindlespeed_entry.setSingleStep(100)
|
||||
grid1.addWidget(self.spindlespeed_entry, 8, 1)
|
||||
|
||||
# Dwell
|
||||
|
@ -1011,6 +1030,12 @@ class ExcellonObjectUI(ObjectUI):
|
|||
self.generate_cnc_button.setToolTip(
|
||||
_("Generate the CNC Job.")
|
||||
)
|
||||
self.generate_cnc_button.setStyleSheet("""
|
||||
QPushButton
|
||||
{
|
||||
font-weight: bold;
|
||||
}
|
||||
""")
|
||||
grid2.addWidget(self.generate_cnc_button, 2, 0, 1, 3)
|
||||
|
||||
# ### Milling Holes Drills ####
|
||||
|
@ -1036,6 +1061,12 @@ class ExcellonObjectUI(ObjectUI):
|
|||
_("Create the Geometry Object\n"
|
||||
"for milling DRILLS toolpaths.")
|
||||
)
|
||||
self.generate_milling_button.setStyleSheet("""
|
||||
QPushButton
|
||||
{
|
||||
font-weight: bold;
|
||||
}
|
||||
""")
|
||||
|
||||
grid2.addWidget(self.tdlabel, 4, 0)
|
||||
grid2.addWidget(self.tooldia_entry, 4, 1)
|
||||
|
@ -1057,6 +1088,12 @@ class ExcellonObjectUI(ObjectUI):
|
|||
_("Create the Geometry Object\n"
|
||||
"for milling SLOTS toolpaths.")
|
||||
)
|
||||
self.generate_milling_slots_button.setStyleSheet("""
|
||||
QPushButton
|
||||
{
|
||||
font-weight: bold;
|
||||
}
|
||||
""")
|
||||
|
||||
grid2.addWidget(self.stdlabel, 5, 0)
|
||||
grid2.addWidget(self.slot_tooldia_entry, 5, 1)
|
||||
|
@ -1501,14 +1538,29 @@ class GeometryObjectUI(ObjectUI):
|
|||
self.cncfeedrate_rapid_entry.hide()
|
||||
|
||||
# Cut over 1st point in path
|
||||
self.extracut_cb = FCCheckBox('%s' % _('Re-cut 1st pt.'))
|
||||
self.extracut_cb = FCCheckBox('%s' % _('Re-cut'))
|
||||
self.extracut_cb.setToolTip(
|
||||
_("In order to remove possible\n"
|
||||
"copper leftovers where first cut\n"
|
||||
"meet with last cut, we generate an\n"
|
||||
"extended cut over the first cut section.")
|
||||
)
|
||||
|
||||
self.e_cut_entry = FCDoubleSpinner()
|
||||
self.e_cut_entry.set_range(0, 99999)
|
||||
self.e_cut_entry.set_precision(self.decimals)
|
||||
self.e_cut_entry.setSingleStep(0.1)
|
||||
self.e_cut_entry.setWrapping(True)
|
||||
self.e_cut_entry.setToolTip(
|
||||
_("In order to remove possible\n"
|
||||
"copper leftovers where first cut\n"
|
||||
"meet with last cut, we generate an\n"
|
||||
"extended cut over the first cut section.")
|
||||
)
|
||||
self.grid3.addWidget(self.extracut_cb, 13, 0)
|
||||
self.grid3.addWidget(self.e_cut_entry, 13, 1)
|
||||
|
||||
self.ois_e_cut = OptionalInputSection(self.extracut_cb, [self.e_cut_entry])
|
||||
|
||||
# Spindlespeed
|
||||
spdlabel = QtWidgets.QLabel('%s:' % _('Spindle speed'))
|
||||
|
@ -1519,7 +1571,9 @@ class GeometryObjectUI(ObjectUI):
|
|||
"this value is the power of laser."
|
||||
)
|
||||
)
|
||||
self.cncspindlespeed_entry = IntEntry(allow_empty=True)
|
||||
self.cncspindlespeed_entry = FCSpinner()
|
||||
self.cncspindlespeed_entry.set_range(0, 1000000)
|
||||
self.cncspindlespeed_entry.setSingleStep(100)
|
||||
|
||||
self.grid3.addWidget(spdlabel, 14, 0)
|
||||
self.grid3.addWidget(self.cncspindlespeed_entry, 14, 1)
|
||||
|
@ -1617,13 +1671,28 @@ class GeometryObjectUI(ObjectUI):
|
|||
self.generate_cnc_button.setToolTip(
|
||||
_("Generate the CNC Job object.")
|
||||
)
|
||||
self.geo_param_box.addWidget(self.generate_cnc_button)
|
||||
self.generate_cnc_button.setStyleSheet("""
|
||||
QPushButton
|
||||
{
|
||||
font-weight: bold;
|
||||
}
|
||||
""")
|
||||
self.grid3.addWidget(self.generate_cnc_button, 23, 0, 1, 2)
|
||||
|
||||
self.grid3.addWidget(QtWidgets.QLabel(''), 24, 0, 1, 2)
|
||||
|
||||
# ##############
|
||||
# Paint area ##
|
||||
# ##############
|
||||
self.paint_label = QtWidgets.QLabel('<b>%s</b>' % _('Paint Area'))
|
||||
self.paint_label.setToolTip(
|
||||
self.tools_label = QtWidgets.QLabel('<b>%s</b>' % _('TOOLS'))
|
||||
self.tools_label.setToolTip(
|
||||
_("Launch Paint Tool in Tools Tab.")
|
||||
)
|
||||
self.grid3.addWidget(self.tools_label, 25, 0, 1, 2)
|
||||
|
||||
# Paint Button
|
||||
self.paint_tool_button = QtWidgets.QPushButton(_('Paint Tool'))
|
||||
self.paint_tool_button.setToolTip(
|
||||
_(
|
||||
"Creates tool paths to cover the\n"
|
||||
"whole area of a polygon (remove\n"
|
||||
|
@ -1631,14 +1700,27 @@ class GeometryObjectUI(ObjectUI):
|
|||
"to click on the desired polygon."
|
||||
)
|
||||
)
|
||||
self.geo_tools_box.addWidget(self.paint_label)
|
||||
self.paint_tool_button.setStyleSheet("""
|
||||
QPushButton
|
||||
{
|
||||
font-weight: bold;
|
||||
}
|
||||
""")
|
||||
self.grid3.addWidget(self.paint_tool_button, 26, 0, 1, 2)
|
||||
|
||||
# GO Button
|
||||
self.paint_tool_button = QtWidgets.QPushButton(_('Paint Tool'))
|
||||
self.paint_tool_button.setToolTip(
|
||||
_("Launch Paint Tool in Tools Tab.")
|
||||
# NCC Tool
|
||||
self.generate_ncc_button = QtWidgets.QPushButton(_('NCC Tool'))
|
||||
self.generate_ncc_button.setToolTip(
|
||||
_("Create the Geometry Object\n"
|
||||
"for non-copper routing.")
|
||||
)
|
||||
self.geo_tools_box.addWidget(self.paint_tool_button)
|
||||
self.generate_ncc_button.setStyleSheet("""
|
||||
QPushButton
|
||||
{
|
||||
font-weight: bold;
|
||||
}
|
||||
""")
|
||||
self.grid3.addWidget(self.generate_ncc_button, 27, 0, 1, 2)
|
||||
|
||||
|
||||
class CNCObjectUI(ObjectUI):
|
||||
|
@ -1781,12 +1863,20 @@ class CNCObjectUI(ObjectUI):
|
|||
|
||||
self.cnc_tools_table.setColumnCount(7)
|
||||
self.cnc_tools_table.setColumnWidth(0, 20)
|
||||
self.cnc_tools_table.setHorizontalHeaderLabels(['#', _('Dia'), _('Offset'), _('Type'), _('TT'), '',
|
||||
_('P')])
|
||||
self.cnc_tools_table.setHorizontalHeaderLabels(['#', _('Dia'), _('Offset'), _('Type'), _('TT'), '', _('P')])
|
||||
self.cnc_tools_table.setColumnHidden(5, True)
|
||||
# stylesheet = "::section{Background-color:rgb(239,239,245)}"
|
||||
# self.cnc_tools_table.horizontalHeader().setStyleSheet(stylesheet)
|
||||
|
||||
self.exc_cnc_tools_table = FCTable()
|
||||
self.custom_box.addWidget(self.exc_cnc_tools_table)
|
||||
|
||||
self.exc_cnc_tools_table.setColumnCount(7)
|
||||
self.exc_cnc_tools_table.setColumnWidth(0, 20)
|
||||
self.exc_cnc_tools_table.setHorizontalHeaderLabels(['#', _('Dia'), _('Drills'), _('Slots'), '', _("Cut Z"),
|
||||
_('P')])
|
||||
self.exc_cnc_tools_table.setColumnHidden(4, True)
|
||||
|
||||
self.tooldia_entry = FCDoubleSpinner()
|
||||
self.tooldia_entry.set_range(0, 9999.9999)
|
||||
self.tooldia_entry.set_precision(self.decimals)
|
||||
|
@ -1820,7 +1910,7 @@ class CNCObjectUI(ObjectUI):
|
|||
|
||||
self.prepend_text = FCTextArea()
|
||||
self.prepend_text.setPlaceholderText(
|
||||
_("Type here any G-Code commands you would "
|
||||
_("Type here any G-Code commands you would\n"
|
||||
"like to add at the beginning of the G-Code file.")
|
||||
)
|
||||
self.custom_box.addWidget(self.prepend_text)
|
||||
|
@ -1836,8 +1926,8 @@ class CNCObjectUI(ObjectUI):
|
|||
|
||||
self.append_text = FCTextArea()
|
||||
self.append_text.setPlaceholderText(
|
||||
_("Type here any G-Code commands you would "
|
||||
"like to append to the generated file. "
|
||||
_("Type here any G-Code commands you would\n"
|
||||
"like to append to the generated file.\n"
|
||||
"I.e.: M2 (End of program)")
|
||||
)
|
||||
self.custom_box.addWidget(self.append_text)
|
||||
|
@ -1868,12 +1958,12 @@ class CNCObjectUI(ObjectUI):
|
|||
self.toolchange_text = FCTextArea()
|
||||
self.toolchange_text.setPlaceholderText(
|
||||
_(
|
||||
"Type here any G-Code commands you would "
|
||||
"like to be executed when Toolchange event is encountered. "
|
||||
"This will constitute a Custom Toolchange GCode, "
|
||||
"or a Toolchange Macro. "
|
||||
"The FlatCAM variables are surrounded by '%' symbol. \n"
|
||||
"WARNING: it can be used only with a preprocessor file "
|
||||
"Type here any G-Code commands you would\n"
|
||||
"like to be executed when Toolchange event is encountered.\n"
|
||||
"This will constitute a Custom Toolchange GCode,\n"
|
||||
"or a Toolchange Macro.\n"
|
||||
"The FlatCAM variables are surrounded by '%' symbol.\n"
|
||||
"WARNING: it can be used only with a preprocessor file\n"
|
||||
"that has 'toolchange_custom' in it's name."
|
||||
)
|
||||
)
|
||||
|
|
|
@ -156,7 +156,7 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
|
|||
self.big_cursor = None
|
||||
# Keep VisPy canvas happy by letting it be "frozen" again.
|
||||
self.freeze()
|
||||
|
||||
self.fit_view()
|
||||
self.graph_event_connect('mouse_wheel', self.on_mouse_scroll)
|
||||
|
||||
def draw_workspace(self, workspace_size):
|
||||
|
@ -303,7 +303,7 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
|
|||
p2 = np.array(curr_pos)[:2]
|
||||
self.view.camera.pan(p2 - p1)
|
||||
|
||||
if self.fcapp.grid_status() == True:
|
||||
if self.fcapp.grid_status():
|
||||
pos_canvas = self.translate_coords(curr_pos)
|
||||
pos = self.fcapp.geo_editor.snap(pos_canvas[0], pos_canvas[1])
|
||||
|
||||
|
|
|
@ -2430,7 +2430,9 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI):
|
|||
"in RPM (optional)")
|
||||
)
|
||||
grid2.addWidget(spdlabel, 6, 0)
|
||||
self.spindlespeed_entry = IntEntry(allow_empty=True)
|
||||
self.spindlespeed_entry = FCSpinner()
|
||||
self.spindlespeed_entry.set_range(0, 1000000)
|
||||
self.spindlespeed_entry.setSingleStep(100)
|
||||
grid2.addWidget(self.spindlespeed_entry, 6, 1)
|
||||
|
||||
# Dwell
|
||||
|
@ -3341,7 +3343,10 @@ class GeometryOptPrefGroupUI(OptionsGroupUI):
|
|||
)
|
||||
)
|
||||
grid1.addWidget(spdlabel, 9, 0)
|
||||
self.cncspindlespeed_entry = IntEntry(allow_empty=True)
|
||||
self.cncspindlespeed_entry = FCSpinner()
|
||||
self.cncspindlespeed_entry.set_range(0, 1000000)
|
||||
self.cncspindlespeed_entry.setSingleStep(100)
|
||||
|
||||
grid1.addWidget(self.cncspindlespeed_entry, 9, 1)
|
||||
|
||||
# Dwell
|
||||
|
@ -3440,14 +3445,27 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
|
|||
grid1.addWidget(self.cncfeedrate_rapid_entry, 4, 1)
|
||||
|
||||
# End move extra cut
|
||||
self.extracut_cb = FCCheckBox(label='%s' % _('Re-cut 1st pt.'))
|
||||
self.extracut_cb = FCCheckBox('%s' % _('Re-cut'))
|
||||
self.extracut_cb.setToolTip(
|
||||
_("In order to remove possible\n"
|
||||
"copper leftovers where first cut\n"
|
||||
"meet with last cut, we generate an\n"
|
||||
"extended cut over the first cut section.")
|
||||
)
|
||||
|
||||
self.e_cut_entry = FCDoubleSpinner()
|
||||
self.e_cut_entry.set_range(0, 99999)
|
||||
self.e_cut_entry.set_precision(self.decimals)
|
||||
self.e_cut_entry.setSingleStep(0.1)
|
||||
self.e_cut_entry.setWrapping(True)
|
||||
self.e_cut_entry.setToolTip(
|
||||
_("In order to remove possible\n"
|
||||
"copper leftovers where first cut\n"
|
||||
"meet with last cut, we generate an\n"
|
||||
"extended cut over the first cut section.")
|
||||
)
|
||||
grid1.addWidget(self.extracut_cb, 5, 0)
|
||||
grid1.addWidget(self.e_cut_entry, 5, 1)
|
||||
|
||||
# Probe depth
|
||||
self.pdepth_label = QtWidgets.QLabel('%s:' % _("Probe Z depth"))
|
||||
|
@ -3762,7 +3780,7 @@ class CNCJobOptPrefGroupUI(OptionsGroupUI):
|
|||
|
||||
self.prepend_text = FCTextArea()
|
||||
self.prepend_text.setPlaceholderText(
|
||||
_("Type here any G-Code commands you would "
|
||||
_("Type here any G-Code commands you would\n"
|
||||
"like to add at the beginning of the G-Code file.")
|
||||
)
|
||||
self.layout.addWidget(self.prepend_text)
|
||||
|
@ -3779,8 +3797,8 @@ class CNCJobOptPrefGroupUI(OptionsGroupUI):
|
|||
|
||||
self.append_text = FCTextArea()
|
||||
self.append_text.setPlaceholderText(
|
||||
_("Type here any G-Code commands you would "
|
||||
"like to append to the generated file. "
|
||||
_("Type here any G-Code commands you would\n"
|
||||
"like to append to the generated file.\n"
|
||||
"I.e.: M2 (End of program)")
|
||||
)
|
||||
self.layout.addWidget(self.append_text)
|
||||
|
@ -3832,12 +3850,12 @@ class CNCJobAdvOptPrefGroupUI(OptionsGroupUI):
|
|||
self.toolchange_text = FCTextArea()
|
||||
self.toolchange_text.setPlaceholderText(
|
||||
_(
|
||||
"Type here any G-Code commands you would "
|
||||
"like to be executed when Toolchange event is encountered. "
|
||||
"This will constitute a Custom Toolchange GCode, "
|
||||
"or a Toolchange Macro. "
|
||||
"The FlatCAM variables are surrounded by '%' symbol. \n"
|
||||
"WARNING: it can be used only with a preprocessor file "
|
||||
"Type here any G-Code commands you would\n"
|
||||
"like to be executed when Toolchange event is encountered.\n"
|
||||
"This will constitute a Custom Toolchange GCode,\n"
|
||||
"or a Toolchange Macro.\n"
|
||||
"The FlatCAM variables are surrounded by '%' symbol.\n"
|
||||
"WARNING: it can be used only with a preprocessor file\n"
|
||||
"that has 'toolchange_custom' in it's name."
|
||||
)
|
||||
)
|
||||
|
@ -4834,7 +4852,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
|
|||
self.orientation_label = QtWidgets.QLabel('%s:' % _("Page Orientation"))
|
||||
self.orientation_label.setToolTip(_("Can be:\n"
|
||||
"- Portrait\n"
|
||||
"- Lanscape"))
|
||||
"- Landscape"))
|
||||
|
||||
self.orientation_radio = RadioSet([{'label': _('Portrait'), 'value': 'p'},
|
||||
{'label': _('Landscape'), 'value': 'l'},
|
||||
|
@ -6105,7 +6123,7 @@ class Tools2CThievingPrefGroupUI(OptionsGroupUI):
|
|||
], orientation='vertical', stretch=False)
|
||||
self.reference_label = QtWidgets.QLabel(_("Reference:"))
|
||||
self.reference_label.setToolTip(
|
||||
_("- 'Itself' - the copper Thieving extent is based on the object that is copper cleared.\n "
|
||||
_("- 'Itself' - the copper Thieving extent is based on the object extent.\n"
|
||||
"- 'Area Selection' - left mouse click to start selection of the area to be filled.\n"
|
||||
"- 'Reference Object' - will do copper thieving within the area specified by another object.")
|
||||
)
|
||||
|
@ -6119,7 +6137,7 @@ class Tools2CThievingPrefGroupUI(OptionsGroupUI):
|
|||
], stretch=False)
|
||||
self.bbox_type_label = QtWidgets.QLabel(_("Box Type:"))
|
||||
self.bbox_type_label.setToolTip(
|
||||
_("- 'Rectangular' - the bounding box will be of rectangular shape.\n "
|
||||
_("- 'Rectangular' - the bounding box will be of rectangular shape.\n"
|
||||
"- 'Minimal' - the bounding box will be the convex hull shape.")
|
||||
)
|
||||
grid_lay.addWidget(self.bbox_type_label, 5, 0)
|
||||
|
@ -6139,7 +6157,7 @@ class Tools2CThievingPrefGroupUI(OptionsGroupUI):
|
|||
], orientation='vertical', stretch=False)
|
||||
self.fill_type_label = QtWidgets.QLabel(_("Fill Type:"))
|
||||
self.fill_type_label.setToolTip(
|
||||
_("- 'Solid' - copper thieving will be a solid polygon.\n "
|
||||
_("- 'Solid' - copper thieving will be a solid polygon.\n"
|
||||
"- 'Dots Grid' - the empty area will be filled with a pattern of dots.\n"
|
||||
"- 'Squares Grid' - the empty area will be filled with a pattern of squares.\n"
|
||||
"- 'Lines Grid' - the empty area will be filled with a pattern of lines.")
|
||||
|
@ -6346,7 +6364,7 @@ class Tools2FiducialsPrefGroupUI(OptionsGroupUI):
|
|||
], stretch=False)
|
||||
self.mode_label = QtWidgets.QLabel(_("Mode:"))
|
||||
self.mode_label.setToolTip(
|
||||
_("- 'Auto' - automatic placement of fiducials in the corners of the bounding box.\n "
|
||||
_("- 'Auto' - automatic placement of fiducials in the corners of the bounding box.\n"
|
||||
"- 'Manual' - manual placement of fiducials.")
|
||||
)
|
||||
grid_lay.addWidget(self.mode_label, 3, 0)
|
||||
|
@ -6361,7 +6379,7 @@ class Tools2FiducialsPrefGroupUI(OptionsGroupUI):
|
|||
self.pos_label = QtWidgets.QLabel('%s:' % _("Second fiducial"))
|
||||
self.pos_label.setToolTip(
|
||||
_("The position for the second fiducial.\n"
|
||||
"- 'Up' - the order is: bottom-left, top-left, top-right.\n "
|
||||
"- 'Up' - the order is: bottom-left, top-left, top-right.\n"
|
||||
"- 'Down' - the order is: bottom-left, bottom-right, top-right.\n"
|
||||
"- 'None' - there is no second fiducial. The order is: bottom-left, top-right.")
|
||||
)
|
||||
|
@ -6495,6 +6513,33 @@ class Tools2CalPrefGroupUI(OptionsGroupUI):
|
|||
grid_lay.addWidget(toolchangez_lbl, 6, 0)
|
||||
grid_lay.addWidget(self.toolchangez_entry, 6, 1, 1, 2)
|
||||
|
||||
# Toolchange X-Y entry
|
||||
toolchangexy_lbl = QtWidgets.QLabel('%s:' % _('Toolchange X-Y'))
|
||||
toolchangexy_lbl.setToolTip(
|
||||
_("Toolchange X,Y position.\n"
|
||||
"If no value is entered then the current\n"
|
||||
"(x, y) point will be used,")
|
||||
)
|
||||
|
||||
self.toolchange_xy_entry = FCEntry()
|
||||
|
||||
grid_lay.addWidget(toolchangexy_lbl, 7, 0)
|
||||
grid_lay.addWidget(self.toolchange_xy_entry, 7, 1, 1, 2)
|
||||
|
||||
# Second point choice
|
||||
second_point_lbl = QtWidgets.QLabel('%s:' % _("Second point"))
|
||||
second_point_lbl.setToolTip(
|
||||
_("Second point in the Gcode verification can be:\n"
|
||||
"- top-left -> the user will align the PCB vertically\n"
|
||||
"- bottom-right -> the user will align the PCB horizontally")
|
||||
)
|
||||
self.second_point_radio = RadioSet([{'label': _('Top-Left'), 'value': 'tl'},
|
||||
{'label': _('Bottom-Right'), 'value': 'br'}],
|
||||
orientation='vertical')
|
||||
|
||||
grid_lay.addWidget(second_point_lbl, 8, 0)
|
||||
grid_lay.addWidget(self.second_point_radio, 8, 1, 1, 2)
|
||||
|
||||
self.layout.addStretch()
|
||||
|
||||
|
||||
|
|
|
@ -108,10 +108,17 @@ class VisPyCanvas(scene.SceneCanvas):
|
|||
# self.measure_fps()
|
||||
|
||||
def translate_coords(self, pos):
|
||||
"""
|
||||
Translate pixels to FlatCAM units.
|
||||
|
||||
"""
|
||||
tr = self.grid.get_transform('canvas', 'visual')
|
||||
return tr.map(pos)
|
||||
|
||||
def translate_coords_2(self, pos):
|
||||
"""
|
||||
Translate FlatCAM units to pixels.
|
||||
"""
|
||||
tr = self.grid.get_transform('visual', 'document')
|
||||
return tr.map(pos)
|
||||
|
||||
|
|
|
@ -94,11 +94,11 @@ class Excellon(Geometry):
|
|||
Geometry.__init__(self, geo_steps_per_circle=int(geo_steps_per_circle))
|
||||
|
||||
# dictionary to store tools, see above for description
|
||||
self.tools = {}
|
||||
self.tools = dict()
|
||||
# list to store the drills, see above for description
|
||||
self.drills = []
|
||||
self.drills = list()
|
||||
# self.slots (list) to store the slots; each is a dictionary
|
||||
self.slots = []
|
||||
self.slots = list()
|
||||
|
||||
self.source_file = ''
|
||||
|
||||
|
@ -109,8 +109,8 @@ class Excellon(Geometry):
|
|||
self.match_routing_start = None
|
||||
self.match_routing_stop = None
|
||||
|
||||
self.num_tools = [] # List for keeping the tools sorted
|
||||
self.index_per_tool = {} # Dictionary to store the indexed points for each tool
|
||||
self.num_tools = list() # List for keeping the tools sorted
|
||||
self.index_per_tool = dict() # Dictionary to store the indexed points for each tool
|
||||
|
||||
# ## IN|MM -> Units are inherited from Geometry
|
||||
self.units = self.app.defaults['units']
|
||||
|
@ -118,8 +118,8 @@ class Excellon(Geometry):
|
|||
# Trailing "T" or leading "L" (default)
|
||||
# self.zeros = "T"
|
||||
self.zeros = zeros or self.defaults["zeros"]
|
||||
self.zeros_found = self.zeros
|
||||
self.units_found = self.units
|
||||
self.zeros_found = deepcopy(self.zeros)
|
||||
self.units_found = deepcopy(self.units)
|
||||
|
||||
# this will serve as a default if the Excellon file has no info regarding of tool diameters (this info may be
|
||||
# in another file like for PCB WIzard ECAD software
|
||||
|
@ -790,7 +790,7 @@ class Excellon(Geometry):
|
|||
# ## Units and number format # ##
|
||||
match = self.units_re.match(eline)
|
||||
if match:
|
||||
self.units = self.units = {"METRIC": "MM", "INCH": "IN"}[match.group(1)]
|
||||
self.units = {"METRIC": "MM", "INCH": "IN"}[match.group(1)]
|
||||
self.zeros = match.group(2) # "T" or "L". Might be empty
|
||||
self.excellon_format = match.group(3)
|
||||
if self.excellon_format:
|
||||
|
@ -884,8 +884,9 @@ class Excellon(Geometry):
|
|||
log.error("Excellon PARSING FAILED. Line %d: %s" % (line_num, eline))
|
||||
msg = '[ERROR_NOTCL] %s' % \
|
||||
_("An internal error has ocurred. See shell.\n")
|
||||
msg += _('{e_code} Excellon Parser error.\nParsing Failed. Line {l_nr}: {line}\n').format(
|
||||
msg += ('{e_code} {tx} {l_nr}: {line}\n').format(
|
||||
e_code='[ERROR]',
|
||||
tx=_("Excellon Parser error.\nParsing Failed. Line"),
|
||||
l_nr=line_num,
|
||||
line=eline)
|
||||
msg += traceback.format_exc()
|
||||
|
|
|
@ -0,0 +1,423 @@
|
|||
# ############################################################
|
||||
# FlatCAM: 2D Post-processing for Manufacturing #
|
||||
# http://flatcam.org #
|
||||
# File Author: Marius Adrian Stanciu (c) #
|
||||
# Date: 12/12/2019 #
|
||||
# MIT Licence #
|
||||
# ############################################################
|
||||
|
||||
from camlib import arc, three_point_circle
|
||||
import FlatCAMApp
|
||||
|
||||
import numpy as np
|
||||
import re
|
||||
import logging
|
||||
import traceback
|
||||
from copy import deepcopy
|
||||
import sys
|
||||
|
||||
from shapely.ops import unary_union
|
||||
from shapely.geometry import LineString, Point
|
||||
|
||||
import FlatCAMTranslation as fcTranslate
|
||||
import gettext
|
||||
import builtins
|
||||
|
||||
if '_' not in builtins.__dict__:
|
||||
_ = gettext.gettext
|
||||
|
||||
log = logging.getLogger('base')
|
||||
|
||||
|
||||
class HPGL2:
|
||||
"""
|
||||
HPGL2 parsing.
|
||||
"""
|
||||
|
||||
def __init__(self, app):
|
||||
"""
|
||||
The constructor takes FlatCAMApp.App as parameter.
|
||||
|
||||
"""
|
||||
self.app = app
|
||||
|
||||
# How to approximate a circle with lines.
|
||||
self.steps_per_circle = int(self.app.defaults["geometry_circle_steps"])
|
||||
self.decimals = self.app.decimals
|
||||
|
||||
# store the file units here
|
||||
self.units = 'MM'
|
||||
|
||||
# storage for the tools
|
||||
self.tools = dict()
|
||||
|
||||
self.default_data = dict()
|
||||
self.default_data.update({
|
||||
"name": '_ncc',
|
||||
"plot": self.app.defaults["geometry_plot"],
|
||||
"cutz": self.app.defaults["geometry_cutz"],
|
||||
"vtipdia": self.app.defaults["geometry_vtipdia"],
|
||||
"vtipangle": self.app.defaults["geometry_vtipangle"],
|
||||
"travelz": self.app.defaults["geometry_travelz"],
|
||||
"feedrate": self.app.defaults["geometry_feedrate"],
|
||||
"feedrate_z": self.app.defaults["geometry_feedrate_z"],
|
||||
"feedrate_rapid": self.app.defaults["geometry_feedrate_rapid"],
|
||||
"dwell": self.app.defaults["geometry_dwell"],
|
||||
"dwelltime": self.app.defaults["geometry_dwelltime"],
|
||||
"multidepth": self.app.defaults["geometry_multidepth"],
|
||||
"ppname_g": self.app.defaults["geometry_ppname_g"],
|
||||
"depthperpass": self.app.defaults["geometry_depthperpass"],
|
||||
"extracut": self.app.defaults["geometry_extracut"],
|
||||
"extracut_length": self.app.defaults["geometry_extracut_length"],
|
||||
"toolchange": self.app.defaults["geometry_toolchange"],
|
||||
"toolchangez": self.app.defaults["geometry_toolchangez"],
|
||||
"endz": self.app.defaults["geometry_endz"],
|
||||
"spindlespeed": self.app.defaults["geometry_spindlespeed"],
|
||||
"toolchangexy": self.app.defaults["geometry_toolchangexy"],
|
||||
"startz": self.app.defaults["geometry_startz"],
|
||||
|
||||
"tooldia": self.app.defaults["tools_painttooldia"],
|
||||
"paintmargin": self.app.defaults["tools_paintmargin"],
|
||||
"paintmethod": self.app.defaults["tools_paintmethod"],
|
||||
"selectmethod": self.app.defaults["tools_selectmethod"],
|
||||
"pathconnect": self.app.defaults["tools_pathconnect"],
|
||||
"paintcontour": self.app.defaults["tools_paintcontour"],
|
||||
"paintoverlap": self.app.defaults["tools_paintoverlap"],
|
||||
|
||||
"nccoverlap": self.app.defaults["tools_nccoverlap"],
|
||||
"nccmargin": self.app.defaults["tools_nccmargin"],
|
||||
"nccmethod": self.app.defaults["tools_nccmethod"],
|
||||
"nccconnect": self.app.defaults["tools_nccconnect"],
|
||||
"ncccontour": self.app.defaults["tools_ncccontour"],
|
||||
"nccrest": self.app.defaults["tools_nccrest"]
|
||||
})
|
||||
|
||||
# will store the geometry here for compatibility reason
|
||||
self.solid_geometry = None
|
||||
|
||||
self.source_file = ''
|
||||
|
||||
# ### Parser patterns ## ##
|
||||
|
||||
# comment
|
||||
self.comment_re = re.compile(r"^CO\s*[\"']([a-zA-Z0-9\s]*)[\"'];?$")
|
||||
|
||||
# select pen
|
||||
self.sp_re = re.compile(r'SP(\d);?$')
|
||||
# pen position
|
||||
self.pen_re = re.compile(r"^(P[U|D]);?$")
|
||||
|
||||
# Initialize
|
||||
self.initialize_re = re.compile(r'^(IN);?$')
|
||||
|
||||
# Absolute linear interpolation
|
||||
self.abs_move_re = re.compile(r"^PA\s*(-?\d+\.?\d+?),?\s*(-?\d+\.?\d+?)*;?$")
|
||||
# Relative linear interpolation
|
||||
self.rel_move_re = re.compile(r"^PR\s*(-?\d+\.\d+?),?\s*(-?\d+\.\d+?)*;?$")
|
||||
|
||||
# Circular interpolation with radius
|
||||
self.circ_re = re.compile(r"^CI\s*(\+?\d+\.?\d+?)?\s*;?\s*$")
|
||||
|
||||
# Arc interpolation with radius
|
||||
self.arc_re = re.compile(r"^AA\s*([+-]?\d+),?\s*([+-]?\d+),?\s*([+-]?\d+);?$")
|
||||
|
||||
# Arc interpolation with 3 points
|
||||
self.arc_3pt_re = re.compile(r"^AT\s*([+-]?\d+),?\s*([+-]?\d+),?\s*([+-]?\d+),?\s*([+-]?\d+);?$")
|
||||
|
||||
self.init_done = None
|
||||
|
||||
def parse_file(self, filename):
|
||||
"""
|
||||
Creates a list of lines from the HPGL2 file and send it to the main parser.
|
||||
|
||||
:param filename: HPGL2 file to parse.
|
||||
:type filename: str
|
||||
:return: None
|
||||
"""
|
||||
|
||||
with open(filename, 'r') as gfile:
|
||||
glines = [line.rstrip('\n') for line in gfile]
|
||||
self.parse_lines(glines=glines)
|
||||
|
||||
def parse_lines(self, glines):
|
||||
"""
|
||||
Main HPGL2 parser.
|
||||
|
||||
:param glines: HPGL2 code as list of strings, each element being
|
||||
one line of the source file.
|
||||
:type glines: list
|
||||
:return: None
|
||||
:rtype: None
|
||||
"""
|
||||
|
||||
# Coordinates of the current path, each is [x, y]
|
||||
path = list()
|
||||
|
||||
geo_buffer = []
|
||||
|
||||
# Current coordinates
|
||||
current_x = None
|
||||
current_y = None
|
||||
|
||||
# Found coordinates
|
||||
linear_x = None
|
||||
linear_y = None
|
||||
|
||||
# store the pen (tool) status
|
||||
pen_status = 'up'
|
||||
|
||||
# store the current tool here
|
||||
current_tool = None
|
||||
|
||||
# ### Parsing starts here ## ##
|
||||
line_num = 0
|
||||
gline = ""
|
||||
|
||||
self.app.inform.emit('%s %d %s.' % (_("HPGL2 processing. Parsing"), len(glines), _("lines")))
|
||||
try:
|
||||
for gline in glines:
|
||||
if self.app.abort_flag:
|
||||
# graceful abort requested by the user
|
||||
raise FlatCAMApp.GracefulException
|
||||
|
||||
line_num += 1
|
||||
self.source_file += gline + '\n'
|
||||
|
||||
# Cleanup #
|
||||
gline = gline.strip(' \r\n')
|
||||
# log.debug("Line=%3s %s" % (line_num, gline))
|
||||
|
||||
# ###################
|
||||
# Ignored lines #####
|
||||
# Comments #####
|
||||
# ###################
|
||||
match = self.comment_re.search(gline)
|
||||
if match:
|
||||
log.debug(str(match.group(1)))
|
||||
continue
|
||||
|
||||
# search for the initialization
|
||||
match = self.initialize_re.search(gline)
|
||||
if match:
|
||||
self.init_done = True
|
||||
continue
|
||||
|
||||
if self.init_done is True:
|
||||
# tools detection
|
||||
match = self.sp_re.search(gline)
|
||||
if match:
|
||||
tool = match.group(1)
|
||||
# self.tools[tool] = dict()
|
||||
self.tools.update({
|
||||
tool: {
|
||||
'tooldia': float('%.*f' %
|
||||
(
|
||||
self.decimals,
|
||||
float(self.app.defaults['geometry_cnctooldia'])
|
||||
)
|
||||
),
|
||||
'offset': 'Path',
|
||||
'offset_value': 0.0,
|
||||
'type': 'Iso',
|
||||
'tool_type': 'C1',
|
||||
'data': deepcopy(self.default_data),
|
||||
'solid_geometry': list()
|
||||
}
|
||||
})
|
||||
|
||||
if current_tool:
|
||||
if path:
|
||||
geo = LineString(path)
|
||||
self.tools[current_tool]['solid_geometry'].append(geo)
|
||||
geo_buffer.append(geo)
|
||||
path[:] = []
|
||||
|
||||
current_tool = tool
|
||||
continue
|
||||
|
||||
# pen status detection
|
||||
match = self.pen_re.search(gline)
|
||||
if match:
|
||||
pen_status = {'PU': 'up', 'PD': 'down'}[match.group(1)]
|
||||
continue
|
||||
|
||||
# Linear interpolation
|
||||
match = self.abs_move_re.search(gline)
|
||||
if match:
|
||||
# Parse coordinates
|
||||
if match.group(1) is not None:
|
||||
linear_x = parse_number(match.group(1))
|
||||
current_x = linear_x
|
||||
else:
|
||||
linear_x = current_x
|
||||
|
||||
if match.group(2) is not None:
|
||||
linear_y = parse_number(match.group(2))
|
||||
current_y = linear_y
|
||||
else:
|
||||
linear_y = current_y
|
||||
|
||||
# Pen down: add segment
|
||||
if pen_status == 'down':
|
||||
# if linear_x or linear_y are None, ignore those
|
||||
if current_x is not None and current_y is not None:
|
||||
# only add the point if it's a new one otherwise skip it (harder to process)
|
||||
if path[-1] != [current_x, current_y]:
|
||||
path.append([current_x, current_y])
|
||||
else:
|
||||
self.app.inform.emit('[WARNING] %s: %s' %
|
||||
(_("Coordinates missing, line ignored"), str(gline)))
|
||||
|
||||
elif pen_status == 'up':
|
||||
if len(path) > 1:
|
||||
geo = LineString(path)
|
||||
self.tools[current_tool]['solid_geometry'].append(geo)
|
||||
geo_buffer.append(geo)
|
||||
path[:] = []
|
||||
|
||||
# if linear_x or linear_y are None, ignore those
|
||||
if linear_x is not None and linear_y is not None:
|
||||
path = [[linear_x, linear_y]] # Start new path
|
||||
else:
|
||||
self.app.inform.emit('[WARNING] %s: %s' %
|
||||
(_("Coordinates missing, line ignored"), str(gline)))
|
||||
|
||||
# log.debug("Line_number=%3s X=%s Y=%s (%s)" % (line_num, linear_x, linear_y, gline))
|
||||
continue
|
||||
|
||||
# Circular interpolation
|
||||
match = self.circ_re.search(gline)
|
||||
if match:
|
||||
if len(path) > 1:
|
||||
geo = LineString(path)
|
||||
self.tools[current_tool]['solid_geometry'].append(geo)
|
||||
geo_buffer.append(geo)
|
||||
path[:] = []
|
||||
|
||||
# if linear_x or linear_y are None, ignore those
|
||||
if linear_x is not None and linear_y is not None:
|
||||
path = [[linear_x, linear_y]] # Start new path
|
||||
else:
|
||||
self.app.inform.emit('[WARNING] %s: %s' %
|
||||
(_("Coordinates missing, line ignored"), str(gline)))
|
||||
|
||||
if current_x is not None and current_y is not None:
|
||||
radius = match.group(1)
|
||||
geo = Point((current_x, current_y)).buffer(radius, int(self.steps_per_circle))
|
||||
geo_line = geo.exterior
|
||||
self.tools[current_tool]['solid_geometry'].append(geo_line)
|
||||
geo_buffer.append(geo_line)
|
||||
continue
|
||||
|
||||
# Arc interpolation with radius
|
||||
match = self.arc_re.search(gline)
|
||||
if match:
|
||||
if len(path) > 1:
|
||||
geo = LineString(path)
|
||||
self.tools[current_tool]['solid_geometry'].append(geo)
|
||||
geo_buffer.append(geo)
|
||||
path[:] = []
|
||||
|
||||
# if linear_x or linear_y are None, ignore those
|
||||
if linear_x is not None and linear_y is not None:
|
||||
path = [[linear_x, linear_y]] # Start new path
|
||||
else:
|
||||
self.app.inform.emit('[WARNING] %s: %s' %
|
||||
(_("Coordinates missing, line ignored"), str(gline)))
|
||||
|
||||
if current_x is not None and current_y is not None:
|
||||
center = [parse_number(match.group(1)), parse_number(match.group(2))]
|
||||
angle = np.deg2rad(float(match.group(3)))
|
||||
p1 = [current_x, current_y]
|
||||
|
||||
arcdir = "ccw" if angle >= 0.0 else "cw"
|
||||
radius = np.sqrt((center[0] - p1[0]) ** 2 + (center[1] - p1[1]) ** 2)
|
||||
startangle = np.arctan2(p1[1] - center[1], p1[0] - center[0])
|
||||
stopangle = startangle + angle
|
||||
|
||||
geo = LineString(arc(center, radius, startangle, stopangle, arcdir, self.steps_per_circle))
|
||||
self.tools[current_tool]['solid_geometry'].append(geo)
|
||||
geo_buffer.append(geo)
|
||||
|
||||
line_coords = list(geo.coords)
|
||||
current_x = line_coords[0]
|
||||
current_y = line_coords[1]
|
||||
continue
|
||||
|
||||
# Arc interpolation with 3 points
|
||||
match = self.arc_3pt_re.search(gline)
|
||||
if match:
|
||||
if len(path) > 1:
|
||||
geo = LineString(path)
|
||||
self.tools[current_tool]['solid_geometry'].append(geo)
|
||||
geo_buffer.append(geo)
|
||||
path[:] = []
|
||||
|
||||
# if linear_x or linear_y are None, ignore those
|
||||
if linear_x is not None and linear_y is not None:
|
||||
path = [[linear_x, linear_y]] # Start new path
|
||||
else:
|
||||
self.app.inform.emit('[WARNING] %s: %s' %
|
||||
(_("Coordinates missing, line ignored"), str(gline)))
|
||||
|
||||
if current_x is not None and current_y is not None:
|
||||
p1 = [current_x, current_y]
|
||||
p3 = [parse_number(match.group(1)), parse_number(match.group(2))]
|
||||
p2 = [parse_number(match.group(3)), parse_number(match.group(4))]
|
||||
|
||||
try:
|
||||
center, radius, t = three_point_circle(p1, p2, p3)
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
direction = 'cw' if np.sign(t) > 0 else 'ccw'
|
||||
|
||||
startangle = np.arctan2(p1[1] - center[1], p1[0] - center[0])
|
||||
stopangle = np.arctan2(p3[1] - center[1], p3[0] - center[0])
|
||||
|
||||
geo = LineString(arc(center, radius, startangle, stopangle,
|
||||
direction, self.steps_per_circle))
|
||||
self.tools[current_tool]['solid_geometry'].append(geo)
|
||||
geo_buffer.append(geo)
|
||||
|
||||
# p2 is the end point for the 3-pt circle
|
||||
current_x = p2[0]
|
||||
current_y = p2[1]
|
||||
continue
|
||||
|
||||
# ## Line did not match any pattern. Warn user.
|
||||
log.warning("Line ignored (%d): %s" % (line_num, gline))
|
||||
|
||||
if not geo_buffer and not self.solid_geometry:
|
||||
log.error("Object is not HPGL2 file or empty. Aborting Object creation.")
|
||||
return 'fail'
|
||||
|
||||
log.warning("Joining %d polygons." % len(geo_buffer))
|
||||
self.app.inform.emit('%s: %d.' % (_("Gerber processing. Joining polygons"), len(geo_buffer)))
|
||||
|
||||
new_poly = unary_union(geo_buffer)
|
||||
self.solid_geometry = new_poly
|
||||
|
||||
except Exception as err:
|
||||
ex_type, ex, tb = sys.exc_info()
|
||||
traceback.print_tb(tb)
|
||||
print(traceback.format_exc())
|
||||
|
||||
log.error("HPGL2 PARSING FAILED. Line %d: %s" % (line_num, gline))
|
||||
|
||||
loc = '%s #%d %s: %s\n' % (_("HPGL2 Line"), line_num, _("HPGL2 Line Content"), gline) + repr(err)
|
||||
self.app.inform.emit('[ERROR] %s\n%s:' % (_("HPGL2 Parser ERROR"), loc))
|
||||
|
||||
|
||||
def parse_number(strnumber):
|
||||
"""
|
||||
Parse a single number of HPGL2 coordinates.
|
||||
|
||||
:param strnumber: String containing a number
|
||||
from a coordinate data block, possibly with a leading sign.
|
||||
:type strnumber: str
|
||||
:return: The number in floating point.
|
||||
:rtype: float
|
||||
"""
|
||||
|
||||
return float(strnumber) / 40.0 # in milimeters
|
|
@ -8,12 +8,13 @@
|
|||
from PyQt5 import QtWidgets, QtCore, QtGui
|
||||
|
||||
from FlatCAMTool import FlatCAMTool
|
||||
from flatcamGUI.GUIElements import FCDoubleSpinner, EvalEntry, FCCheckBox, OptionalInputSection
|
||||
from flatcamGUI.GUIElements import FCDoubleSpinner, EvalEntry, FCCheckBox, OptionalInputSection, FCEntry
|
||||
from flatcamGUI.GUIElements import FCTable, FCComboBox, RadioSet
|
||||
from flatcamEditors.FlatCAMTextEditor import TextEditor
|
||||
|
||||
from shapely.geometry import Point
|
||||
from shapely.geometry.base import *
|
||||
from shapely.affinity import scale, skew
|
||||
|
||||
import math
|
||||
from datetime import datetime
|
||||
|
@ -63,13 +64,119 @@ class ToolCalibration(FlatCAMTool):
|
|||
grid_lay.setColumnStretch(1, 1)
|
||||
grid_lay.setColumnStretch(2, 0)
|
||||
|
||||
self.gcode_title_label = QtWidgets.QLabel('<b>%s</b>' % _('GCode Parameters'))
|
||||
self.gcode_title_label.setToolTip(
|
||||
_("Parameters used when creating the GCode in this tool.")
|
||||
)
|
||||
grid_lay.addWidget(self.gcode_title_label, 0, 0, 1, 3)
|
||||
|
||||
# Travel Z entry
|
||||
travelz_lbl = QtWidgets.QLabel('%s:' % _("Travel Z"))
|
||||
travelz_lbl.setToolTip(
|
||||
_("Height (Z) for travelling between the points.")
|
||||
)
|
||||
|
||||
self.travelz_entry = FCDoubleSpinner()
|
||||
self.travelz_entry.set_range(-9999.9999, 9999.9999)
|
||||
self.travelz_entry.set_precision(self.decimals)
|
||||
self.travelz_entry.setSingleStep(0.1)
|
||||
|
||||
grid_lay.addWidget(travelz_lbl, 1, 0)
|
||||
grid_lay.addWidget(self.travelz_entry, 1, 1, 1, 2)
|
||||
|
||||
# Verification Z entry
|
||||
verz_lbl = QtWidgets.QLabel('%s:' % _("Verification Z"))
|
||||
verz_lbl.setToolTip(
|
||||
_("Height (Z) for checking the point.")
|
||||
)
|
||||
|
||||
self.verz_entry = FCDoubleSpinner()
|
||||
self.verz_entry.set_range(-9999.9999, 9999.9999)
|
||||
self.verz_entry.set_precision(self.decimals)
|
||||
self.verz_entry.setSingleStep(0.1)
|
||||
|
||||
grid_lay.addWidget(verz_lbl, 2, 0)
|
||||
grid_lay.addWidget(self.verz_entry, 2, 1, 1, 2)
|
||||
|
||||
# Zero the Z of the verification tool
|
||||
self.zeroz_cb = FCCheckBox('%s' % _("Zero Z tool"))
|
||||
self.zeroz_cb.setToolTip(
|
||||
_("Include a sequence to zero the height (Z)\n"
|
||||
"of the verification tool.")
|
||||
)
|
||||
|
||||
grid_lay.addWidget(self.zeroz_cb, 3, 0, 1, 3)
|
||||
|
||||
# Toolchange Z entry
|
||||
toolchangez_lbl = QtWidgets.QLabel('%s:' % _("Toolchange Z"))
|
||||
toolchangez_lbl.setToolTip(
|
||||
_("Height (Z) for mounting the verification probe.")
|
||||
)
|
||||
|
||||
self.toolchangez_entry = FCDoubleSpinner()
|
||||
self.toolchangez_entry.set_range(0.0000, 9999.9999)
|
||||
self.toolchangez_entry.set_precision(self.decimals)
|
||||
self.toolchangez_entry.setSingleStep(0.1)
|
||||
|
||||
grid_lay.addWidget(toolchangez_lbl, 4, 0)
|
||||
grid_lay.addWidget(self.toolchangez_entry, 4, 1, 1, 2)
|
||||
|
||||
# Toolchange X-Y entry
|
||||
toolchangexy_lbl = QtWidgets.QLabel('%s:' % _('Toolchange X-Y'))
|
||||
toolchangexy_lbl.setToolTip(
|
||||
_("Toolchange X,Y position.\n"
|
||||
"If no value is entered then the current\n"
|
||||
"(x, y) point will be used,")
|
||||
)
|
||||
|
||||
self.toolchange_xy_entry = FCEntry()
|
||||
|
||||
grid_lay.addWidget(toolchangexy_lbl, 5, 0)
|
||||
grid_lay.addWidget(self.toolchange_xy_entry, 5, 1, 1, 2)
|
||||
|
||||
self.z_ois = OptionalInputSection(
|
||||
self.zeroz_cb,
|
||||
[
|
||||
toolchangez_lbl,
|
||||
self.toolchangez_entry,
|
||||
toolchangexy_lbl,
|
||||
self.toolchange_xy_entry
|
||||
]
|
||||
)
|
||||
|
||||
separator_line1 = QtWidgets.QFrame()
|
||||
separator_line1.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
separator_line1.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
grid_lay.addWidget(separator_line1, 6, 0, 1, 3)
|
||||
|
||||
# Second point choice
|
||||
second_point_lbl = QtWidgets.QLabel('%s:' % _("Second point"))
|
||||
second_point_lbl.setToolTip(
|
||||
_("Second point in the Gcode verification can be:\n"
|
||||
"- top-left -> the user will align the PCB vertically\n"
|
||||
"- bottom-right -> the user will align the PCB horizontally")
|
||||
)
|
||||
self.second_point_radio = RadioSet([{'label': _('Top-Left'), 'value': 'tl'},
|
||||
{'label': _('Bottom-Right'), 'value': 'br'}],
|
||||
orientation='vertical')
|
||||
|
||||
grid_lay.addWidget(second_point_lbl, 7, 0)
|
||||
grid_lay.addWidget(self.second_point_radio, 7, 1, 1, 2)
|
||||
|
||||
separator_line1 = QtWidgets.QFrame()
|
||||
separator_line1.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
separator_line1.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
grid_lay.addWidget(separator_line1, 8, 0, 1, 3)
|
||||
|
||||
grid_lay.addWidget(QtWidgets.QLabel(''), 9, 0, 1, 3)
|
||||
|
||||
step_1 = QtWidgets.QLabel('<b>%s</b>' % _("STEP 1: Acquire Calibration Points"))
|
||||
step_1.setToolTip(
|
||||
_("Pick four points by clicking inside the drill holes.\n"
|
||||
_("Pick four points by clicking on canvas.\n"
|
||||
"Those four points should be in the four\n"
|
||||
"(as much as possible) corners of the Excellon object.")
|
||||
"(as much as possible) corners of the object.")
|
||||
)
|
||||
grid_lay.addWidget(step_1, 0, 0, 1, 3)
|
||||
grid_lay.addWidget(step_1, 10, 0, 1, 3)
|
||||
|
||||
self.cal_source_lbl = QtWidgets.QLabel("<b>%s:</b>" % _("Source Type"))
|
||||
self.cal_source_lbl.setToolTip(_("The source of calibration points.\n"
|
||||
|
@ -80,8 +187,8 @@ class ToolCalibration(FlatCAMTool):
|
|||
{'label': _('Free'), 'value': 'free'}],
|
||||
stretch=False)
|
||||
|
||||
grid_lay.addWidget(self.cal_source_lbl, 1, 0)
|
||||
grid_lay.addWidget(self.cal_source_radio, 1, 1, 1, 2)
|
||||
grid_lay.addWidget(self.cal_source_lbl, 11, 0)
|
||||
grid_lay.addWidget(self.cal_source_radio, 11, 1, 1, 2)
|
||||
|
||||
self.obj_type_label = QtWidgets.QLabel("%s:" % _("Object Type"))
|
||||
|
||||
|
@ -90,8 +197,11 @@ class ToolCalibration(FlatCAMTool):
|
|||
self.obj_type_combo.addItem(_("Excellon"))
|
||||
self.obj_type_combo.setCurrentIndex(1)
|
||||
|
||||
grid_lay.addWidget(self.obj_type_label, 2, 0)
|
||||
grid_lay.addWidget(self.obj_type_combo, 2, 1, 1, 2)
|
||||
self.obj_type_combo.setItemIcon(0, QtGui.QIcon("share/flatcam_icon16.png"))
|
||||
self.obj_type_combo.setItemIcon(1, QtGui.QIcon("share/drill16.png"))
|
||||
|
||||
grid_lay.addWidget(self.obj_type_label, 12, 0)
|
||||
grid_lay.addWidget(self.obj_type_combo, 12, 1, 1, 2)
|
||||
|
||||
self.object_combo = FCComboBox()
|
||||
self.object_combo.setModel(self.app.collection)
|
||||
|
@ -103,20 +213,20 @@ class ToolCalibration(FlatCAMTool):
|
|||
_("FlatCAM Object to be used as a source for reference points.")
|
||||
)
|
||||
|
||||
grid_lay.addWidget(self.object_label, 3, 0, 1, 3)
|
||||
grid_lay.addWidget(self.object_combo, 4, 0, 1, 3)
|
||||
grid_lay.addWidget(self.object_label, 13, 0, 1, 3)
|
||||
grid_lay.addWidget(self.object_combo, 14, 0, 1, 3)
|
||||
|
||||
self.points_table_label = QtWidgets.QLabel('<b>%s</b>' % _('Calibration Points'))
|
||||
self.points_table_label.setToolTip(
|
||||
_("Contain the expected calibration points and the\n"
|
||||
"ones measured.")
|
||||
)
|
||||
grid_lay.addWidget(self.points_table_label, 5, 0, 1, 3)
|
||||
grid_lay.addWidget(self.points_table_label, 15, 0, 1, 3)
|
||||
|
||||
self.points_table = FCTable()
|
||||
self.points_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
||||
# self.points_table.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
|
||||
grid_lay.addWidget(self.points_table, 6, 0, 1, 3)
|
||||
grid_lay.addWidget(self.points_table, 16, 0, 1, 3)
|
||||
|
||||
self.points_table.setColumnCount(4)
|
||||
self.points_table.setHorizontalHeaderLabels(
|
||||
|
@ -220,6 +330,7 @@ class ToolCalibration(FlatCAMTool):
|
|||
self.points_table.setCellWidget(row, 2, self.top_right_coordx_tgt)
|
||||
self.top_right_coordx_tgt.setReadOnly(True)
|
||||
self.top_right_coordx_found = EvalEntry()
|
||||
self.top_right_coordx_found.setDisabled(True)
|
||||
self.points_table.setCellWidget(row, 3, self.top_right_coordx_found)
|
||||
row += 1
|
||||
|
||||
|
@ -229,6 +340,7 @@ class ToolCalibration(FlatCAMTool):
|
|||
self.points_table.setCellWidget(row, 2, self.top_right_coordy_tgt)
|
||||
self.top_right_coordy_tgt.setReadOnly(True)
|
||||
self.top_right_coordy_found = EvalEntry()
|
||||
self.top_right_coordy_found.setDisabled(True)
|
||||
self.points_table.setCellWidget(row, 3, self.top_right_coordy_found)
|
||||
|
||||
vertical_header = self.points_table.verticalHeader()
|
||||
|
@ -268,87 +380,38 @@ class ToolCalibration(FlatCAMTool):
|
|||
font-weight: bold;
|
||||
}
|
||||
""")
|
||||
grid_lay.addWidget(self.start_button, 7, 0, 1, 3)
|
||||
grid_lay.addWidget(self.start_button, 17, 0, 1, 3)
|
||||
|
||||
separator_line = QtWidgets.QFrame()
|
||||
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
grid_lay.addWidget(separator_line, 8, 0, 1, 3)
|
||||
grid_lay.addWidget(separator_line, 18, 0, 1, 3)
|
||||
|
||||
grid_lay.addWidget(QtWidgets.QLabel(''), 9, 0)
|
||||
grid_lay.addWidget(QtWidgets.QLabel(''), 19, 0)
|
||||
|
||||
# STEP 2 #
|
||||
step_2 = QtWidgets.QLabel('<b>%s</b>' % _("STEP 2: Verification GCode"))
|
||||
step_2.setToolTip(
|
||||
_("Generate GCode file to locate and align the PCB by using\n"
|
||||
"the four points acquired above.")
|
||||
"the four points acquired above.\n"
|
||||
"The points sequence is:\n"
|
||||
"- first point -> set the origin\n"
|
||||
"- second point -> alignment point. Can be: top-left or bottom-right.\n"
|
||||
"- third point -> check point. Can be: top-left or bottom-right.\n"
|
||||
"- forth point -> final verification point. Just for evaluation.")
|
||||
)
|
||||
grid_lay.addWidget(step_2, 10, 0, 1, 3)
|
||||
|
||||
self.gcode_title_label = QtWidgets.QLabel('<b>%s</b>' % _('GCode Parameters'))
|
||||
self.gcode_title_label.setToolTip(
|
||||
_("Parameters used when creating the GCode in this tool.")
|
||||
)
|
||||
grid_lay.addWidget(self.gcode_title_label, 11, 0, 1, 3)
|
||||
|
||||
# Travel Z entry
|
||||
travelz_lbl = QtWidgets.QLabel('%s:' % _("Travel Z"))
|
||||
travelz_lbl.setToolTip(
|
||||
_("Height (Z) for travelling between the points.")
|
||||
)
|
||||
|
||||
self.travelz_entry = FCDoubleSpinner()
|
||||
self.travelz_entry.set_range(-9999.9999, 9999.9999)
|
||||
self.travelz_entry.set_precision(self.decimals)
|
||||
self.travelz_entry.setSingleStep(0.1)
|
||||
|
||||
grid_lay.addWidget(travelz_lbl, 12, 0)
|
||||
grid_lay.addWidget(self.travelz_entry, 12, 1, 1, 2)
|
||||
|
||||
# Verification Z entry
|
||||
verz_lbl = QtWidgets.QLabel('%s:' % _("Verification Z"))
|
||||
verz_lbl.setToolTip(
|
||||
_("Height (Z) for checking the point.")
|
||||
)
|
||||
|
||||
self.verz_entry = FCDoubleSpinner()
|
||||
self.verz_entry.set_range(-9999.9999, 9999.9999)
|
||||
self.verz_entry.set_precision(self.decimals)
|
||||
self.verz_entry.setSingleStep(0.1)
|
||||
|
||||
grid_lay.addWidget(verz_lbl, 13, 0)
|
||||
grid_lay.addWidget(self.verz_entry, 13, 1, 1, 2)
|
||||
|
||||
# Zero the Z of the verification tool
|
||||
self.zeroz_cb = FCCheckBox('%s' % _("Zero Z tool"))
|
||||
self.zeroz_cb.setToolTip(
|
||||
_("Include a sequence to zero the height (Z)\n"
|
||||
"of the verification tool.")
|
||||
)
|
||||
|
||||
grid_lay.addWidget(self.zeroz_cb, 14, 0, 1, 3)
|
||||
|
||||
# Toochange Z entry
|
||||
toolchangez_lbl = QtWidgets.QLabel('%s:' % _("Toolchange Z"))
|
||||
toolchangez_lbl.setToolTip(
|
||||
_("Height (Z) for mounting the verification probe.")
|
||||
)
|
||||
|
||||
self.toolchangez_entry = FCDoubleSpinner()
|
||||
self.toolchangez_entry.set_range(0.0000, 9999.9999)
|
||||
self.toolchangez_entry.set_precision(self.decimals)
|
||||
self.toolchangez_entry.setSingleStep(0.1)
|
||||
|
||||
grid_lay.addWidget(toolchangez_lbl, 15, 0)
|
||||
grid_lay.addWidget(self.toolchangez_entry, 15, 1, 1, 2)
|
||||
|
||||
self.z_ois = OptionalInputSection(self.zeroz_cb, [toolchangez_lbl, self.toolchangez_entry])
|
||||
grid_lay.addWidget(step_2, 20, 0, 1, 3)
|
||||
|
||||
# ## GCode Button
|
||||
self.gcode_button = QtWidgets.QPushButton(_("Generate GCode"))
|
||||
self.gcode_button.setToolTip(
|
||||
_("Generate GCode file to locate and align the PCB by using\n"
|
||||
"the four points acquired above.")
|
||||
"the four points acquired above.\n"
|
||||
"The points sequence is:\n"
|
||||
"- first point -> set the origin\n"
|
||||
"- second point -> alignment point. Can be: top-left or bottom-right.\n"
|
||||
"- third point -> check point. Can be: top-left or bottom-right.\n"
|
||||
"- forth point -> final verification point. Just for evaluation.")
|
||||
)
|
||||
self.gcode_button.setStyleSheet("""
|
||||
QPushButton
|
||||
|
@ -356,14 +419,14 @@ class ToolCalibration(FlatCAMTool):
|
|||
font-weight: bold;
|
||||
}
|
||||
""")
|
||||
grid_lay.addWidget(self.gcode_button, 16, 0, 1, 3)
|
||||
grid_lay.addWidget(self.gcode_button, 21, 0, 1, 3)
|
||||
|
||||
separator_line1 = QtWidgets.QFrame()
|
||||
separator_line1.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
separator_line1.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
grid_lay.addWidget(separator_line1, 17, 0, 1, 3)
|
||||
grid_lay.addWidget(separator_line1, 22, 0, 1, 3)
|
||||
|
||||
grid_lay.addWidget(QtWidgets.QLabel(''), 18, 0, 1, 3)
|
||||
grid_lay.addWidget(QtWidgets.QLabel(''), 23, 0, 1, 3)
|
||||
|
||||
# STEP 3 #
|
||||
step_3 = QtWidgets.QLabel('<b>%s</b>' % _("STEP 3: Adjustments"))
|
||||
|
@ -372,7 +435,7 @@ class ToolCalibration(FlatCAMTool):
|
|||
"found when checking the PCB pattern. The differences must be filled\n"
|
||||
"in the fields Found (Delta).")
|
||||
)
|
||||
grid_lay.addWidget(step_3, 19, 0, 1, 3)
|
||||
grid_lay.addWidget(step_3, 24, 0, 1, 3)
|
||||
|
||||
# ## Factors Button
|
||||
self.generate_factors_button = QtWidgets.QPushButton(_("Calculate Factors"))
|
||||
|
@ -387,14 +450,14 @@ class ToolCalibration(FlatCAMTool):
|
|||
font-weight: bold;
|
||||
}
|
||||
""")
|
||||
grid_lay.addWidget(self.generate_factors_button, 20, 0, 1, 3)
|
||||
grid_lay.addWidget(self.generate_factors_button, 25, 0, 1, 3)
|
||||
|
||||
separator_line1 = QtWidgets.QFrame()
|
||||
separator_line1.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
separator_line1.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
grid_lay.addWidget(separator_line1, 21, 0, 1, 3)
|
||||
grid_lay.addWidget(separator_line1, 26, 0, 1, 3)
|
||||
|
||||
grid_lay.addWidget(QtWidgets.QLabel(''), 22, 0, 1, 3)
|
||||
grid_lay.addWidget(QtWidgets.QLabel(''), 27, 0, 1, 3)
|
||||
|
||||
# STEP 4 #
|
||||
step_4 = QtWidgets.QLabel('<b>%s</b>' % _("STEP 4: Adjusted GCode"))
|
||||
|
@ -402,7 +465,7 @@ class ToolCalibration(FlatCAMTool):
|
|||
_("Generate verification GCode file adjusted with\n"
|
||||
"the factors above.")
|
||||
)
|
||||
grid_lay.addWidget(step_4, 23, 0, 1, 3)
|
||||
grid_lay.addWidget(step_4, 28, 0, 1, 3)
|
||||
|
||||
self.scalex_label = QtWidgets.QLabel(_("Scale Factor X:"))
|
||||
self.scalex_label.setToolTip(
|
||||
|
@ -413,8 +476,8 @@ class ToolCalibration(FlatCAMTool):
|
|||
self.scalex_entry.set_precision(self.decimals)
|
||||
self.scalex_entry.setSingleStep(0.1)
|
||||
|
||||
grid_lay.addWidget(self.scalex_label, 24, 0)
|
||||
grid_lay.addWidget(self.scalex_entry, 24, 1, 1, 2)
|
||||
grid_lay.addWidget(self.scalex_label, 29, 0)
|
||||
grid_lay.addWidget(self.scalex_entry, 29, 1, 1, 2)
|
||||
|
||||
self.scaley_label = QtWidgets.QLabel(_("Scale Factor Y:"))
|
||||
self.scaley_label.setToolTip(
|
||||
|
@ -425,8 +488,8 @@ class ToolCalibration(FlatCAMTool):
|
|||
self.scaley_entry.set_precision(self.decimals)
|
||||
self.scaley_entry.setSingleStep(0.1)
|
||||
|
||||
grid_lay.addWidget(self.scaley_label, 25, 0)
|
||||
grid_lay.addWidget(self.scaley_entry, 25, 1, 1, 2)
|
||||
grid_lay.addWidget(self.scaley_label, 30, 0)
|
||||
grid_lay.addWidget(self.scaley_entry, 30, 1, 1, 2)
|
||||
|
||||
self.scale_button = QtWidgets.QPushButton(_("Apply Scale Factors"))
|
||||
self.scale_button.setToolTip(
|
||||
|
@ -438,7 +501,7 @@ class ToolCalibration(FlatCAMTool):
|
|||
font-weight: bold;
|
||||
}
|
||||
""")
|
||||
grid_lay.addWidget(self.scale_button, 26, 0, 1, 3)
|
||||
grid_lay.addWidget(self.scale_button, 31, 0, 1, 3)
|
||||
|
||||
self.skewx_label = QtWidgets.QLabel(_("Skew Angle X:"))
|
||||
self.skewx_label.setToolTip(
|
||||
|
@ -450,8 +513,8 @@ class ToolCalibration(FlatCAMTool):
|
|||
self.skewx_entry.set_precision(self.decimals)
|
||||
self.skewx_entry.setSingleStep(0.1)
|
||||
|
||||
grid_lay.addWidget(self.skewx_label, 27, 0)
|
||||
grid_lay.addWidget(self.skewx_entry, 27, 1, 1, 2)
|
||||
grid_lay.addWidget(self.skewx_label, 32, 0)
|
||||
grid_lay.addWidget(self.skewx_entry, 32, 1, 1, 2)
|
||||
|
||||
self.skewy_label = QtWidgets.QLabel(_("Skew Angle Y:"))
|
||||
self.skewy_label.setToolTip(
|
||||
|
@ -463,8 +526,8 @@ class ToolCalibration(FlatCAMTool):
|
|||
self.skewy_entry.set_precision(self.decimals)
|
||||
self.skewy_entry.setSingleStep(0.1)
|
||||
|
||||
grid_lay.addWidget(self.skewy_label, 28, 0)
|
||||
grid_lay.addWidget(self.skewy_entry, 28, 1, 1, 2)
|
||||
grid_lay.addWidget(self.skewy_label, 33, 0)
|
||||
grid_lay.addWidget(self.skewy_entry, 33, 1, 1, 2)
|
||||
|
||||
self.skew_button = QtWidgets.QPushButton(_("Apply Skew Factors"))
|
||||
self.skew_button.setToolTip(
|
||||
|
@ -476,7 +539,7 @@ class ToolCalibration(FlatCAMTool):
|
|||
font-weight: bold;
|
||||
}
|
||||
""")
|
||||
grid_lay.addWidget(self.skew_button, 29, 0, 1, 3)
|
||||
grid_lay.addWidget(self.skew_button, 34, 0, 1, 3)
|
||||
|
||||
# final_factors_lbl = QtWidgets.QLabel('<b>%s</b>' % _("Final Factors"))
|
||||
# final_factors_lbl.setToolTip(
|
||||
|
@ -540,7 +603,9 @@ class ToolCalibration(FlatCAMTool):
|
|||
self.adj_gcode_button = QtWidgets.QPushButton(_("Generate Adjusted GCode"))
|
||||
self.adj_gcode_button.setToolTip(
|
||||
_("Generate verification GCode file adjusted with\n"
|
||||
"the factors above.")
|
||||
"the factors set above.\n"
|
||||
"The GCode parameters can be readjusted\n"
|
||||
"before clicking this button.")
|
||||
)
|
||||
self.adj_gcode_button.setStyleSheet("""
|
||||
QPushButton
|
||||
|
@ -548,14 +613,14 @@ class ToolCalibration(FlatCAMTool):
|
|||
font-weight: bold;
|
||||
}
|
||||
""")
|
||||
grid_lay.addWidget(self.adj_gcode_button, 35, 0, 1, 3)
|
||||
grid_lay.addWidget(self.adj_gcode_button, 42, 0, 1, 3)
|
||||
|
||||
separator_line1 = QtWidgets.QFrame()
|
||||
separator_line1.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
separator_line1.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
grid_lay.addWidget(separator_line1, 36, 0, 1, 3)
|
||||
grid_lay.addWidget(separator_line1, 43, 0, 1, 3)
|
||||
|
||||
grid_lay.addWidget(QtWidgets.QLabel(''), 37, 0, 1, 3)
|
||||
grid_lay.addWidget(QtWidgets.QLabel(''), 44, 0, 1, 3)
|
||||
|
||||
# STEP 5 #
|
||||
step_5 = QtWidgets.QLabel('<b>%s</b>' % _("STEP 5: Calibrate FlatCAM Objects"))
|
||||
|
@ -563,19 +628,23 @@ class ToolCalibration(FlatCAMTool):
|
|||
_("Adjust the FlatCAM objects\n"
|
||||
"with the factors determined and verified above.")
|
||||
)
|
||||
grid_lay.addWidget(step_5, 38, 0, 1, 3)
|
||||
grid_lay.addWidget(step_5, 45, 0, 1, 3)
|
||||
|
||||
self.adj_object_type_combo = QtWidgets.QComboBox()
|
||||
self.adj_object_type_combo.addItems([_("Gerber"), _("Excellon"), _("Geometry")])
|
||||
self.adj_object_type_combo.setCurrentIndex(0)
|
||||
|
||||
self.adj_object_type_combo.setItemIcon(0, QtGui.QIcon("share/flatcam_icon16.png"))
|
||||
self.adj_object_type_combo.setItemIcon(1, QtGui.QIcon("share/drill16.png"))
|
||||
self.adj_object_type_combo.setItemIcon(2, QtGui.QIcon("share/geometry16.png"))
|
||||
|
||||
self.adj_object_type_label = QtWidgets.QLabel("%s:" % _("Adjusted object type"))
|
||||
self.adj_object_type_label.setToolTip(
|
||||
_("Type of the FlatCAM Object to be adjusted.")
|
||||
)
|
||||
|
||||
grid_lay.addWidget(self.adj_object_type_label, 39, 0, 1, 3)
|
||||
grid_lay.addWidget(self.adj_object_type_combo, 40, 0, 1, 3)
|
||||
grid_lay.addWidget(self.adj_object_type_label, 46, 0, 1, 3)
|
||||
grid_lay.addWidget(self.adj_object_type_combo, 47, 0, 1, 3)
|
||||
|
||||
self.adj_object_combo = FCComboBox()
|
||||
self.adj_object_combo.setModel(self.app.collection)
|
||||
|
@ -587,8 +656,8 @@ class ToolCalibration(FlatCAMTool):
|
|||
_("The FlatCAM Object to be adjusted.")
|
||||
)
|
||||
|
||||
grid_lay.addWidget(self.adj_object_label, 41, 0, 1, 3)
|
||||
grid_lay.addWidget(self.adj_object_combo, 42, 0, 1, 3)
|
||||
grid_lay.addWidget(self.adj_object_label, 48, 0, 1, 3)
|
||||
grid_lay.addWidget(self.adj_object_combo, 49, 0, 1, 3)
|
||||
|
||||
# ## Adjust Objects Button
|
||||
self.cal_button = QtWidgets.QPushButton(_("Calibrate"))
|
||||
|
@ -602,14 +671,14 @@ class ToolCalibration(FlatCAMTool):
|
|||
font-weight: bold;
|
||||
}
|
||||
""")
|
||||
grid_lay.addWidget(self.cal_button, 43, 0, 1, 3)
|
||||
grid_lay.addWidget(self.cal_button, 50, 0, 1, 3)
|
||||
|
||||
separator_line2 = QtWidgets.QFrame()
|
||||
separator_line2.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
separator_line2.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
grid_lay.addWidget(separator_line2, 44, 0, 1, 3)
|
||||
grid_lay.addWidget(separator_line2, 51, 0, 1, 3)
|
||||
|
||||
grid_lay.addWidget(QtWidgets.QLabel(''), 45, 0, 1, 3)
|
||||
grid_lay.addWidget(QtWidgets.QLabel(''), 52, 0, 1, 3)
|
||||
|
||||
self.layout.addStretch()
|
||||
|
||||
|
@ -630,7 +699,7 @@ class ToolCalibration(FlatCAMTool):
|
|||
self.units = ''
|
||||
|
||||
# here store 4 points to be used for calibration
|
||||
self.click_points = list()
|
||||
self.click_points = [[], [], [], []]
|
||||
|
||||
# store the status of the grid
|
||||
self.grid_status_memory = None
|
||||
|
@ -647,17 +716,22 @@ class ToolCalibration(FlatCAMTool):
|
|||
self.cal_object = None
|
||||
|
||||
# ## Signals
|
||||
self.start_button.clicked.connect(self.on_start_collect_points)
|
||||
self.gcode_button.clicked.connect(self.generate_verification_gcode)
|
||||
self.generate_factors_button.clicked.connect(self.calculate_factors)
|
||||
self.reset_button.clicked.connect(self.set_tool_ui)
|
||||
|
||||
self.cal_source_radio.activated_custom.connect(self.on_cal_source_radio)
|
||||
|
||||
self.obj_type_combo.currentIndexChanged.connect(self.on_obj_type_combo)
|
||||
self.adj_object_type_combo.currentIndexChanged.connect(self.on_adj_obj_type_combo)
|
||||
|
||||
self.start_button.clicked.connect(self.on_start_collect_points)
|
||||
|
||||
self.gcode_button.clicked.connect(self.generate_verification_gcode)
|
||||
self.adj_gcode_button.clicked.connect(self.generate_verification_gcode)
|
||||
|
||||
self.generate_factors_button.clicked.connect(self.calculate_factors)
|
||||
|
||||
self.scale_button.clicked.connect(self.on_scale_button)
|
||||
self.skew_button.clicked.connect(self.on_skew_button)
|
||||
|
||||
self.cal_button.clicked.connect(self.on_cal_button_click)
|
||||
self.reset_button.clicked.connect(self.set_tool_ui)
|
||||
|
||||
def run(self, toggle=True):
|
||||
self.app.report_usage("ToolCalibration()")
|
||||
|
@ -685,7 +759,7 @@ class ToolCalibration(FlatCAMTool):
|
|||
|
||||
self.set_tool_ui()
|
||||
|
||||
self.app.ui.notebook.setTabText(2, _("Calibrate Tool"))
|
||||
self.app.ui.notebook.setTabText(2, _("Calibration Tool"))
|
||||
|
||||
def install(self, icon=None, separator=None, **kwargs):
|
||||
FlatCAMTool.install(self, icon, separator, shortcut='ALT+E', **kwargs)
|
||||
|
@ -703,6 +777,9 @@ class ToolCalibration(FlatCAMTool):
|
|||
self.verz_entry.set_value(self.app.defaults['tools_cal_verz'])
|
||||
self.zeroz_cb.set_value(self.app.defaults['tools_cal_zeroz'])
|
||||
self.toolchangez_entry.set_value(self.app.defaults['tools_cal_toolchangez'])
|
||||
self.toolchange_xy_entry.set_value(self.app.defaults['tools_cal_toolchange_xy'])
|
||||
|
||||
self.second_point_radio.set_value(self.app.defaults['tools_cal_sec_point'])
|
||||
|
||||
self.scalex_entry.set_value(1.0)
|
||||
self.scaley_entry.set_value(1.0)
|
||||
|
@ -743,7 +820,7 @@ class ToolCalibration(FlatCAMTool):
|
|||
model_index = self.app.collection.index(selection_index, 0, self.object_combo.rootModelIndex())
|
||||
try:
|
||||
self.target_obj = model_index.internalPointer().obj
|
||||
except Exception:
|
||||
except AttributeError:
|
||||
self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no source FlatCAM object selected..."))
|
||||
return
|
||||
|
||||
|
@ -768,60 +845,75 @@ class ToolCalibration(FlatCAMTool):
|
|||
self.app.inform.emit(_("Get First calibration point. Bottom Left..."))
|
||||
|
||||
def on_mouse_click_release(self, event):
|
||||
if event.button == 1:
|
||||
if self.app.is_legacy is False:
|
||||
event_pos = event.pos
|
||||
else:
|
||||
event_pos = (event.xdata, event.ydata)
|
||||
if self.app.is_legacy is False:
|
||||
event_pos = event.pos
|
||||
right_button = 2
|
||||
self.app.event_is_dragging = self.app.event_is_dragging
|
||||
else:
|
||||
event_pos = (event.xdata, event.ydata)
|
||||
right_button = 3
|
||||
self.app.event_is_dragging = self.app.ui.popMenu.mouse_is_panning
|
||||
|
||||
pos_canvas = self.canvas.translate_coords(event_pos)
|
||||
pos_canvas = self.canvas.translate_coords(event_pos)
|
||||
|
||||
if event.button == 1:
|
||||
click_pt = Point([pos_canvas[0], pos_canvas[1]])
|
||||
|
||||
if self.cal_source_radio.get_value() == 'object':
|
||||
if self.target_obj.kind.lower() == 'excellon':
|
||||
for tool, tool_dict in self.target_obj.tools.items():
|
||||
for geo in tool_dict['solid_geometry']:
|
||||
if click_pt.within(geo):
|
||||
center_pt = geo.centroid
|
||||
self.click_points.append(
|
||||
(
|
||||
float('%.*f' % (self.decimals, center_pt.x)),
|
||||
float('%.*f' % (self.decimals, center_pt.y))
|
||||
)
|
||||
)
|
||||
self.check_points()
|
||||
else:
|
||||
for apid, apid_val in self.target_obj.apertures.items():
|
||||
for geo_el in apid_val['geometry']:
|
||||
if 'solid' in geo_el:
|
||||
if click_pt.within(geo_el['solid']):
|
||||
if isinstance(geo_el['follow'], Point):
|
||||
center_pt = geo_el['solid'].centroid
|
||||
self.click_points.append(
|
||||
(
|
||||
float('%.*f' % (self.decimals, center_pt.x)),
|
||||
float('%.*f' % (self.decimals, center_pt.y))
|
||||
)
|
||||
)
|
||||
self.check_points()
|
||||
if self.app.selection_type is not None:
|
||||
# delete previous selection shape
|
||||
self.app.delete_selection_shape()
|
||||
self.app.selection_type = None
|
||||
else:
|
||||
self.click_points.append(
|
||||
(
|
||||
float('%.*f' % (self.decimals, click_pt.x)),
|
||||
float('%.*f' % (self.decimals, click_pt.y))
|
||||
if self.cal_source_radio.get_value() == 'object':
|
||||
if self.target_obj.kind.lower() == 'excellon':
|
||||
for tool, tool_dict in self.target_obj.tools.items():
|
||||
for geo in tool_dict['solid_geometry']:
|
||||
if click_pt.within(geo):
|
||||
center_pt = geo.centroid
|
||||
self.click_points.append(
|
||||
[
|
||||
float('%.*f' % (self.decimals, center_pt.x)),
|
||||
float('%.*f' % (self.decimals, center_pt.y))
|
||||
]
|
||||
)
|
||||
self.check_points()
|
||||
else:
|
||||
for apid, apid_val in self.target_obj.apertures.items():
|
||||
for geo_el in apid_val['geometry']:
|
||||
if 'solid' in geo_el:
|
||||
if click_pt.within(geo_el['solid']):
|
||||
if isinstance(geo_el['follow'], Point):
|
||||
center_pt = geo_el['solid'].centroid
|
||||
self.click_points.append(
|
||||
[
|
||||
float('%.*f' % (self.decimals, center_pt.x)),
|
||||
float('%.*f' % (self.decimals, center_pt.y))
|
||||
]
|
||||
)
|
||||
self.check_points()
|
||||
else:
|
||||
self.click_points.append(
|
||||
[
|
||||
float('%.*f' % (self.decimals, click_pt.x)),
|
||||
float('%.*f' % (self.decimals, click_pt.y))
|
||||
]
|
||||
)
|
||||
)
|
||||
self.check_points()
|
||||
self.check_points()
|
||||
elif event.button == right_button and self.app.event_is_dragging is False:
|
||||
if len(self.click_points) != 4:
|
||||
self.reset_calibration_points()
|
||||
self.disconnect_cal_events()
|
||||
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled by user request."))
|
||||
|
||||
def check_points(self):
|
||||
if len(self.click_points) == 1:
|
||||
self.bottom_left_coordx_tgt.set_value(self.click_points[0][0])
|
||||
self.bottom_left_coordy_tgt.set_value(self.click_points[0][1])
|
||||
self.app.inform.emit(_("Get Second calibration point. Bottom Right..."))
|
||||
self.app.inform.emit(_("Get Second calibration point. Bottom Right (Top Left)..."))
|
||||
elif len(self.click_points) == 2:
|
||||
self.bottom_right_coordx_tgt.set_value(self.click_points[1][0])
|
||||
self.bottom_right_coordy_tgt.set_value(self.click_points[1][1])
|
||||
self.app.inform.emit(_("Get Third calibration point. Top Left..."))
|
||||
self.app.inform.emit(_("Get Third calibration point. Top Left (Bottom Right)..."))
|
||||
elif len(self.click_points) == 3:
|
||||
self.top_left_coordx_tgt.set_value(self.click_points[2][0])
|
||||
self.top_left_coordy_tgt.set_value(self.click_points[2][1])
|
||||
|
@ -847,6 +939,12 @@ class ToolCalibration(FlatCAMTool):
|
|||
self.top_right_coordx_tgt.set_value('')
|
||||
self.top_right_coordy_tgt.set_value('')
|
||||
|
||||
self.bottom_right_coordx_found.set_value('')
|
||||
self.bottom_right_coordy_found.set_value('')
|
||||
|
||||
self.top_left_coordx_found.set_value('')
|
||||
self.top_left_coordy_found.set_value('')
|
||||
|
||||
def gcode_header(self):
|
||||
log.debug("ToolCalibration.gcode_header()")
|
||||
time_str = "{:%A, %d %B %Y at %H:%M}".format(datetime.now())
|
||||
|
@ -854,10 +952,10 @@ class ToolCalibration(FlatCAMTool):
|
|||
gcode = '(G-CODE GENERATED BY FLATCAM v%s - www.flatcam.org - Version Date: %s)\n' % \
|
||||
(str(self.app.version), str(self.app.version_date)) + '\n'
|
||||
|
||||
gcode += '(Name: ' + _('Verification GCode for FlatCAM Calibrate Tool') + ')\n'
|
||||
gcode += '(Name: ' + _('Verification GCode for FlatCAM Calibration Tool') + ')\n'
|
||||
|
||||
gcode += '(Units: ' + self.units.upper() + ')\n' + "\n"
|
||||
gcode += '(Created on ' + time_str + ')\n' + '\n'
|
||||
gcode += '(Units: ' + self.units.upper() + ')\n\n'
|
||||
gcode += '(Created on ' + time_str + ')\n\n'
|
||||
gcode += 'G20\n' if self.units.upper() == 'IN' else 'G21\n'
|
||||
gcode += 'G90\n'
|
||||
gcode += 'G17\n'
|
||||
|
@ -872,9 +970,13 @@ class ToolCalibration(FlatCAMTool):
|
|||
self.app.ui.plot_tab_area.removeTab(idx)
|
||||
|
||||
def generate_verification_gcode(self):
|
||||
sec_point = self.second_point_radio.get_value()
|
||||
|
||||
travel_z = '%.*f' % (self.decimals, self.travelz_entry.get_value())
|
||||
toolchange_z = '%.*f' % (self.decimals, self.toolchangez_entry.get_value())
|
||||
toolchange_xy_temp = self.toolchange_xy_entry.get_value().split(",")
|
||||
toolchange_xy = [float(eval(a)) for a in toolchange_xy_temp if a != '']
|
||||
|
||||
verification_z = '%.*f' % (self.decimals, self.verz_entry.get_value())
|
||||
|
||||
if len(self.click_points) != 4:
|
||||
|
@ -884,36 +986,64 @@ class ToolCalibration(FlatCAMTool):
|
|||
gcode = self.gcode_header()
|
||||
if self.zeroz_cb.get_value():
|
||||
gcode += 'M5\n'
|
||||
gcode += f'G00 Z{toolchange_z}\n'
|
||||
gcode += 'G00 Z%s\n' % toolchange_z
|
||||
if toolchange_xy:
|
||||
gcode += 'G00 X%s Y%s\n' % (toolchange_xy[0], toolchange_xy[1])
|
||||
gcode += 'M0\n'
|
||||
gcode += 'G01 Z0\n'
|
||||
gcode += 'M0\n'
|
||||
gcode += f'G00 Z{toolchange_z}\n'
|
||||
gcode += 'G00 Z%s\n' % toolchange_z
|
||||
gcode += 'M0\n'
|
||||
|
||||
gcode += f'G00 Z{travel_z}\n'
|
||||
gcode += f'G00 X{self.click_points[0][0]} Y{self.click_points[0][1]}\n'
|
||||
gcode += f'G01 Z{verification_z}\n'
|
||||
# first point: bottom - left -> ORIGIN set
|
||||
gcode += 'G00 Z%s\n' % travel_z
|
||||
gcode += 'G00 X%s Y%s\n' % (self.click_points[0][0], self.click_points[0][1])
|
||||
gcode += 'G01 Z%s\n' % verification_z
|
||||
gcode += 'M0\n'
|
||||
|
||||
gcode += f'G00 Z{travel_z}\n'
|
||||
gcode += f'G00 X{self.click_points[2][0]} Y{self.click_points[2][1]}\n'
|
||||
gcode += f'G01 Z{verification_z}\n'
|
||||
gcode += 'M0\n'
|
||||
if sec_point == 'tl':
|
||||
# second point: top - left -> align the PCB to this point
|
||||
gcode += 'G00 Z%s\n' % travel_z
|
||||
gcode += 'G00 X%s Y%s\n' % (self.click_points[2][0], self.click_points[2][1])
|
||||
gcode += 'G01 Z%s\n' % verification_z
|
||||
gcode += 'M0\n'
|
||||
|
||||
gcode += f'G00 Z{travel_z}\n'
|
||||
gcode += f'G00 X{self.click_points[3][0]} Y{self.click_points[3][1]}\n'
|
||||
gcode += f'G01 Z{verification_z}\n'
|
||||
gcode += 'M0\n'
|
||||
# third point: bottom - right -> check for scale on X axis or for skew on Y axis
|
||||
gcode += 'G00 Z%s\n' % travel_z
|
||||
gcode += 'G00 X%s Y%s\n' % (self.click_points[1][0], self.click_points[1][1])
|
||||
gcode += 'G01 Z%s\n' % verification_z
|
||||
gcode += 'M0\n'
|
||||
|
||||
gcode += f'G00 Z{travel_z}\n'
|
||||
gcode += f'G00 X{self.click_points[1][0]} Y{self.click_points[1][1]}\n'
|
||||
gcode += f'G01 Z{verification_z}\n'
|
||||
gcode += 'M0\n'
|
||||
# forth point: top - right -> verification point
|
||||
gcode += 'G00 Z%s\n' % travel_z
|
||||
gcode += 'G00 X%s Y%s\n' % (self.click_points[3][0], self.click_points[3][1])
|
||||
gcode += 'G01 Z%s\n' % verification_z
|
||||
gcode += 'M0\n'
|
||||
else:
|
||||
# second point: bottom - right -> align the PCB to this point
|
||||
gcode += 'G00 Z%s\n' % travel_z
|
||||
gcode += 'G00 X%s Y%s\n' % (self.click_points[1][0], self.click_points[1][1])
|
||||
gcode += 'G01 Z%s\n' % verification_z
|
||||
gcode += 'M0\n'
|
||||
|
||||
gcode += f'G00 Z{travel_z}\n'
|
||||
gcode += f'G00 X0 Y0\n'
|
||||
gcode += f'G00 Z{toolchange_z}\n'
|
||||
# third point: top - left -> check for scale on Y axis or for skew on X axis
|
||||
gcode += 'G00 Z%s\n' % travel_z
|
||||
gcode += 'G00 X%s Y%s\n' % (self.click_points[2][0], self.click_points[2][1])
|
||||
gcode += 'G01 Z%s\n' % verification_z
|
||||
gcode += 'M0\n'
|
||||
|
||||
# forth point: top - right -> verification point
|
||||
gcode += 'G00 Z%s\n' % travel_z
|
||||
gcode += 'G00 X%s Y%s\n' % (self.click_points[3][0], self.click_points[3][1])
|
||||
gcode += 'G01 Z%s\n' % verification_z
|
||||
gcode += 'M0\n'
|
||||
|
||||
# return to (toolchange_xy[0], toolchange_xy[1], toolchange_z) point for toolchange event
|
||||
gcode += 'G00 Z%s\n' % travel_z
|
||||
gcode += 'G00 X0 Y0\n'
|
||||
gcode += 'G00 Z%s\n' % toolchange_z
|
||||
if toolchange_xy:
|
||||
gcode += 'G00 X%s Y%s\n' % (toolchange_xy[0], toolchange_xy[1])
|
||||
|
||||
gcode += 'M2'
|
||||
|
||||
|
@ -961,74 +1091,142 @@ class ToolCalibration(FlatCAMTool):
|
|||
origin_x = self.click_points[0][0]
|
||||
origin_y = self.click_points[0][1]
|
||||
|
||||
top_left_x = float('%.*f' % (self.decimals, self.click_points[2][0]))
|
||||
top_left_y = float('%.*f' % (self.decimals, self.click_points[2][1]))
|
||||
top_left_x = self.click_points[2][0]
|
||||
top_left_y = self.click_points[2][1]
|
||||
|
||||
bot_right_x = self.click_points[1][0]
|
||||
bot_right_y = self.click_points[1][1]
|
||||
|
||||
try:
|
||||
top_left_dx = float('%.*f' % (self.decimals, self.top_left_coordx_found.get_value()))
|
||||
top_left_dx = float(self.top_left_coordx_found.get_value())
|
||||
except TypeError:
|
||||
top_left_dx = top_left_x
|
||||
|
||||
try:
|
||||
top_left_dy = float('%.*f' % (self.decimals, self.top_left_coordy_found.get_value()))
|
||||
top_left_dy = float(self.top_left_coordy_found.get_value())
|
||||
except TypeError:
|
||||
top_left_dy = top_left_y
|
||||
|
||||
# top_right_x = float('%.*f' % (self.decimals, self.click_points[3][0]))
|
||||
# top_right_y = float('%.*f' % (self.decimals, self.click_points[3][1]))
|
||||
|
||||
# try:
|
||||
# top_right_dx = float('%.*f' % (self.decimals, self.top_right_coordx_found.get_value()))
|
||||
# except TypeError:
|
||||
# top_right_dx = top_right_x
|
||||
#
|
||||
# try:
|
||||
# top_right_dy = float('%.*f' % (self.decimals, self.top_right_coordy_found.get_value()))
|
||||
# except TypeError:
|
||||
# top_right_dy = top_right_y
|
||||
|
||||
bot_right_x = float('%.*f' % (self.decimals, self.click_points[1][0]))
|
||||
bot_right_y = float('%.*f' % (self.decimals, self.click_points[1][1]))
|
||||
|
||||
try:
|
||||
bot_right_dx = float('%.*f' % (self.decimals, self.bottom_right_coordx_found.get_value()))
|
||||
bot_right_dx = float(self.bottom_right_coordx_found.get_value())
|
||||
except TypeError:
|
||||
bot_right_dx = bot_right_x
|
||||
|
||||
try:
|
||||
bot_right_dy = float('%.*f' % (self.decimals, self.bottom_right_coordy_found.get_value()))
|
||||
bot_right_dy = float(self.bottom_right_coordy_found.get_value())
|
||||
except TypeError:
|
||||
bot_right_dy = bot_right_y
|
||||
|
||||
# ------------------------------------------------------------------------------- #
|
||||
# --------------------------- FACTORS CALCULUS ---------------------------------- #
|
||||
# ------------------------------------------------------------------------------- #
|
||||
if top_left_dy != float('%.*f' % (self.decimals, 0.0)):
|
||||
if bot_right_dx != float('%.*f' % (self.decimals, bot_right_x)):
|
||||
# we have scale on X
|
||||
scale_x = (bot_right_dx / (bot_right_x - origin_x)) + 1
|
||||
self.scalex_entry.set_value(scale_x)
|
||||
|
||||
if top_left_dy != float('%.*f' % (self.decimals, top_left_y)):
|
||||
# we have scale on Y
|
||||
scale_y = (top_left_dy + top_left_y - origin_y) / (top_left_y - origin_y)
|
||||
scale_y = (top_left_dy / (top_left_y - origin_y)) + 1
|
||||
self.scaley_entry.set_value(scale_y)
|
||||
|
||||
if top_left_dx != float('%.*f' % (self.decimals, 0.0)):
|
||||
if top_left_dx != float('%.*f' % (self.decimals, top_left_x)):
|
||||
# we have skew on X
|
||||
dx = top_left_dx
|
||||
dy = top_left_y - origin_y
|
||||
skew_angle_x = math.degrees(math.atan(dx / dy))
|
||||
|
||||
self.skewx_entry.set_value(skew_angle_x)
|
||||
|
||||
if bot_right_dx != float('%.*f' % (self.decimals, 0.0)):
|
||||
# we have scale on X
|
||||
scale_x = (bot_right_dx + bot_right_x - origin_x) / (bot_right_x - origin_x)
|
||||
self.scalex_entry.set_value(scale_x)
|
||||
|
||||
if bot_right_dy != float('%.*f' % (self.decimals, 0.0)):
|
||||
if bot_right_dy != float('%.*f' % (self.decimals, bot_right_y)):
|
||||
# we have skew on Y
|
||||
dx = bot_right_x - origin_x
|
||||
dy = bot_right_dy + origin_y
|
||||
skew_angle_y = math.degrees(math.atan(dy / dx))
|
||||
|
||||
self.skewy_entry.set_value(skew_angle_y)
|
||||
|
||||
@property
|
||||
def target_values_in_table(self):
|
||||
self.click_points[0][0] = self.bottom_left_coordx_tgt.get_value()
|
||||
self.click_points[0][1] = self.bottom_left_coordy_tgt.get_value()
|
||||
|
||||
self.click_points[1][0] = self.bottom_right_coordx_tgt.get_value()
|
||||
self.click_points[1][1] = self.bottom_right_coordy_tgt.get_value()
|
||||
|
||||
self.click_points[2][0] = self.top_left_coordx_tgt.get_value()
|
||||
self.click_points[2][1] = self.top_left_coordy_tgt.get_value()
|
||||
|
||||
self.click_points[3][0] = self.top_right_coordx_tgt.get_value()
|
||||
self.click_points[3][1] = self.top_right_coordy_tgt.get_value()
|
||||
|
||||
return self.click_points
|
||||
|
||||
@target_values_in_table.setter
|
||||
def target_values_in_table(self, param):
|
||||
bl_pt, br_pt, tl_pt, tr_pt = param
|
||||
|
||||
self.click_points[0] = [bl_pt[0], bl_pt[1]]
|
||||
self.click_points[1] = [br_pt[0], br_pt[1]]
|
||||
self.click_points[2] = [tl_pt[0], tl_pt[1]]
|
||||
self.click_points[3] = [tr_pt[0], tr_pt[1]]
|
||||
|
||||
self.bottom_left_coordx_tgt.set_value(float('%.*f' % (self.decimals, bl_pt[0])))
|
||||
self.bottom_left_coordy_tgt.set_value(float('%.*f' % (self.decimals, bl_pt[1])))
|
||||
|
||||
self.bottom_right_coordx_tgt.set_value(float('%.*f' % (self.decimals, br_pt[0])))
|
||||
self.bottom_right_coordy_tgt.set_value(float('%.*f' % (self.decimals, br_pt[1])))
|
||||
|
||||
self.top_left_coordx_tgt.set_value(float('%.*f' % (self.decimals, tl_pt[0])))
|
||||
self.top_left_coordy_tgt.set_value(float('%.*f' % (self.decimals, tl_pt[1])))
|
||||
|
||||
self.top_right_coordx_tgt.set_value(float('%.*f' % (self.decimals, tr_pt[0])))
|
||||
self.top_right_coordy_tgt.set_value(float('%.*f' % (self.decimals, tr_pt[1])))
|
||||
|
||||
def on_scale_button(self):
|
||||
scalex_fact = self.scalex_entry.get_value()
|
||||
scaley_fact = self.scaley_entry.get_value()
|
||||
bl, br, tl, tr = self.target_values_in_table
|
||||
|
||||
bl_geo = Point(bl[0], bl[1])
|
||||
br_geo = Point(br[0], br[1])
|
||||
tl_geo = Point(tl[0], tl[1])
|
||||
tr_geo = Point(tr[0], tr[1])
|
||||
|
||||
bl_scaled = scale(bl_geo, xfact=scalex_fact, yfact=scaley_fact, origin=(bl[0], bl[1]))
|
||||
br_scaled = scale(br_geo, xfact=scalex_fact, yfact=scaley_fact, origin=(bl[0], bl[1]))
|
||||
tl_scaled = scale(tl_geo, xfact=scalex_fact, yfact=scaley_fact, origin=(bl[0], bl[1]))
|
||||
tr_scaled = scale(tr_geo, xfact=scalex_fact, yfact=scaley_fact, origin=(bl[0], bl[1]))
|
||||
|
||||
scaled_values = [
|
||||
[bl_scaled.x, bl_scaled.y],
|
||||
[br_scaled.x, br_scaled.y],
|
||||
[tl_scaled.x, tl_scaled.y],
|
||||
[tr_scaled.x, tr_scaled.y]
|
||||
]
|
||||
self.target_values_in_table = scaled_values
|
||||
|
||||
def on_skew_button(self):
|
||||
skewx_angle = self.skewx_entry.get_value()
|
||||
skewy_angle = self.skewy_entry.get_value()
|
||||
bl, br, tl, tr = self.target_values_in_table
|
||||
|
||||
bl_geo = Point(bl[0], bl[1])
|
||||
br_geo = Point(br[0], br[1])
|
||||
tl_geo = Point(tl[0], tl[1])
|
||||
tr_geo = Point(tr[0], tr[1])
|
||||
|
||||
bl_skewed = skew(bl_geo, xs=skewx_angle, ys=skewy_angle, origin=(bl[0], bl[1]))
|
||||
br_skewed = skew(br_geo, xs=skewx_angle, ys=skewy_angle, origin=(bl[0], bl[1]))
|
||||
tl_skewed = skew(tl_geo, xs=skewx_angle, ys=skewy_angle, origin=(bl[0], bl[1]))
|
||||
tr_skewed = skew(tr_geo, xs=skewx_angle, ys=skewy_angle, origin=(bl[0], bl[1]))
|
||||
|
||||
skewed_values = [
|
||||
[bl_skewed.x, bl_skewed.y],
|
||||
[br_skewed.x, br_skewed.y],
|
||||
[tl_skewed.x, tl_skewed.y],
|
||||
[tr_skewed.x, tr_skewed.y]
|
||||
]
|
||||
self.target_values_in_table = skewed_values
|
||||
|
||||
def on_cal_button_click(self):
|
||||
# get the FlatCAM object to calibrate
|
||||
selection_index = self.adj_object_combo.currentIndex()
|
||||
|
@ -1076,8 +1274,8 @@ class ToolCalibration(FlatCAMTool):
|
|||
try:
|
||||
if obj.tools:
|
||||
obj_init.tools = deepcopy(obj.tools)
|
||||
except Exception as e:
|
||||
log.debug("App.on_copy_object() --> %s" % str(e))
|
||||
except Exception as ee:
|
||||
log.debug("App.on_copy_object() --> %s" % str(ee))
|
||||
|
||||
obj_init.scale(xfactor=scalex, yfactor=scaley, point=(origin_x, origin_y))
|
||||
obj_init.skew(angle_x=skewx, angle_y=skewy, point=(origin_x, origin_y))
|
||||
|
@ -1102,8 +1300,8 @@ class ToolCalibration(FlatCAMTool):
|
|||
try:
|
||||
if obj.tools:
|
||||
obj_init.tools = deepcopy(obj.tools)
|
||||
except Exception as e:
|
||||
log.debug("App.on_copy_object() --> %s" % str(e))
|
||||
except Exception as err:
|
||||
log.debug("App.on_copy_object() --> %s" % str(err))
|
||||
|
||||
obj_init.scale(xfactor=scalex, yfactor=scaley, point=(origin_x, origin_y))
|
||||
obj_init.skew(angle_x=skewx, angle_y=skewy, point=(origin_x, origin_y))
|
||||
|
|
|
@ -128,7 +128,7 @@ class ToolCopperThieving(FlatCAMTool):
|
|||
], orientation='vertical', stretch=False)
|
||||
self.reference_label = QtWidgets.QLabel(_("Reference:"))
|
||||
self.reference_label.setToolTip(
|
||||
_("- 'Itself' - the copper thieving extent is based on the object that is copper cleared.\n"
|
||||
_("- 'Itself' - the copper thieving extent is based on the object extent.\n"
|
||||
"- 'Area Selection' - left mouse click to start selection of the area to be filled.\n"
|
||||
"- 'Reference Object' - will do copper thieving within the area specified by another object.")
|
||||
)
|
||||
|
|
|
@ -153,7 +153,7 @@ class DblSidedTool(FlatCAMTool):
|
|||
# ## Axis Location
|
||||
self.axis_location = RadioSet([{'label': _('Point'), 'value': 'point'},
|
||||
{'label': _('Box'), 'value': 'box'}])
|
||||
self.axloc_label = QtWidgets.QLabel(_("Axis Ref:"))
|
||||
self.axloc_label = QtWidgets.QLabel('%s:' % _("Axis Ref"))
|
||||
self.axloc_label.setToolTip(
|
||||
_("The axis should pass through a <b>point</b> or cut\n "
|
||||
"a specified <b>box</b> (in a FlatCAM object) through \n"
|
||||
|
|
|
@ -349,7 +349,10 @@ class Distance(FlatCAMTool):
|
|||
d = math.sqrt(dx ** 2 + dy ** 2)
|
||||
self.stop_entry.set_value("(%.*f, %.*f)" % (self.decimals, pos[0], self.decimals, pos[1]))
|
||||
|
||||
self.app.inform.emit(_("MEASURING: Result D(x) = {d_x} | D(y) = {d_y} | Distance = {d_z}").format(
|
||||
self.app.inform.emit("{tx1}: {tx2} D(x) = {d_x} | D(y) = {d_y} | (tx3} = {d_z}".format(
|
||||
tx1=_("MEASURING"),
|
||||
tx2=_("Result"),
|
||||
tx3=_("Distance"),
|
||||
d_x='%*f' % (self.decimals, abs(dx)),
|
||||
d_y='%*f' % (self.decimals, abs(dy)),
|
||||
d_z='%*f' % (self.decimals, abs(d)))
|
||||
|
|
|
@ -278,7 +278,10 @@ class DistanceMin(FlatCAMTool):
|
|||
)
|
||||
|
||||
if d != 0:
|
||||
self.app.inform.emit(_("MEASURING: Result D(x) = {d_x} | D(y) = {d_y} | Distance = {d_z}").format(
|
||||
self.app.inform.emit("{tx1}: {tx2} D(x) = {d_x} | D(y) = {d_y} | (tx3} = {d_z}".format(
|
||||
tx1=_("MEASURING"),
|
||||
tx2=_("Result"),
|
||||
tx3=_("Distance"),
|
||||
d_x='%*f' % (self.decimals, abs(dx)),
|
||||
d_y='%*f' % (self.decimals, abs(dy)),
|
||||
d_z='%*f' % (self.decimals, abs(d)))
|
||||
|
|
|
@ -203,7 +203,7 @@ class ToolFiducials(FlatCAMTool):
|
|||
self.pos_label = QtWidgets.QLabel('%s:' % _("Second fiducial"))
|
||||
self.pos_label.setToolTip(
|
||||
_("The position for the second fiducial.\n"
|
||||
"- 'Up' - the order is: bottom-left, top-left, top-right.\n "
|
||||
"- 'Up' - the order is: bottom-left, top-left, top-right.\n"
|
||||
"- 'Down' - the order is: bottom-left, bottom-right, top-right.\n"
|
||||
"- 'None' - there is no second fiducial. The order is: bottom-left, top-right.")
|
||||
)
|
||||
|
|
|
@ -19,7 +19,7 @@ from reportlab.graphics import renderPDF
|
|||
from reportlab.pdfgen import canvas
|
||||
from reportlab.graphics import renderPM
|
||||
from reportlab.lib.units import inch, mm
|
||||
from reportlab.lib.pagesizes import landscape, portrait, A4
|
||||
from reportlab.lib.pagesizes import landscape, portrait
|
||||
|
||||
from svglib.svglib import svg2rlg
|
||||
from xml.dom.minidom import parseString as parse_xml_string
|
||||
|
@ -669,6 +669,10 @@ class Film(FlatCAMTool):
|
|||
_("No FlatCAM object selected. Load an object for Box and retry."))
|
||||
return
|
||||
|
||||
if name == '' or boxname == '':
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _("No FlatCAM object selected."))
|
||||
return
|
||||
|
||||
scale_stroke_width = float(self.film_scale_stroke_entry.get_value())
|
||||
source = self.source_punch.get_value()
|
||||
file_type = self.file_type_radio.get_value()
|
||||
|
@ -738,12 +742,18 @@ class Film(FlatCAMTool):
|
|||
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Export positive film cancelled."))
|
||||
return
|
||||
else:
|
||||
pagesize = self.pagesize_combo.get_value()
|
||||
orientation = self.orientation_radio.get_value()
|
||||
color = self.app.defaults['tools_film_color']
|
||||
|
||||
self.export_positive(name, boxname, filename,
|
||||
scale_stroke_factor=factor,
|
||||
scale_factor_x=scale_factor_x, scale_factor_y=scale_factor_y,
|
||||
skew_factor_x=skew_factor_x, skew_factor_y=skew_factor_y,
|
||||
skew_reference=skew_reference,
|
||||
mirror=mirror, ftype=ftype
|
||||
mirror=mirror,
|
||||
pagesize=pagesize, orientation=orientation, color=color, opacity=1.0,
|
||||
ftype=ftype
|
||||
)
|
||||
|
||||
def generate_positive_punched_film(self, name, boxname, source, factor, ftype='svg'):
|
||||
|
@ -1068,7 +1078,7 @@ class Film(FlatCAMTool):
|
|||
scale_stroke_factor=0.00,
|
||||
scale_factor_x=None, scale_factor_y=None,
|
||||
skew_factor_x=None, skew_factor_y=None, skew_reference='center',
|
||||
mirror=None,
|
||||
mirror=None, orientation_val='p', pagesize_val='A4', color_val='black', opacity_val=1.0,
|
||||
use_thread=True, ftype='svg'):
|
||||
"""
|
||||
Exports a Geometry Object to an SVG file in positive black.
|
||||
|
@ -1112,7 +1122,12 @@ class Film(FlatCAMTool):
|
|||
self.inform.emit('[WARNING_NOTCL] %s: %s' % (_("No object Box. Using instead"), obj))
|
||||
box = obj
|
||||
|
||||
def make_positive_film():
|
||||
p_size = pagesize_val
|
||||
orientation = orientation_val
|
||||
color = color_val
|
||||
transparency_level = opacity_val
|
||||
|
||||
def make_positive_film(p_size, orientation, color, transparency_level):
|
||||
log.debug("FilmTool.export_positive().make_positive_film()")
|
||||
|
||||
exported_svg = obj.export_svg(scale_stroke_factor=scale_stroke_factor,
|
||||
|
@ -1127,9 +1142,9 @@ class Film(FlatCAMTool):
|
|||
# We set the colour to WHITE
|
||||
root = ET.fromstring(exported_svg)
|
||||
for child in root:
|
||||
child.set('fill', str(self.app.defaults['tools_film_color']))
|
||||
child.set('opacity', '1.0')
|
||||
child.set('stroke', str(self.app.defaults['tools_film_color']))
|
||||
child.set('fill', str(color))
|
||||
child.set('opacity', str(transparency_level))
|
||||
child.set('stroke', str(color))
|
||||
|
||||
exported_svg = ET.tostring(root)
|
||||
|
||||
|
@ -1190,7 +1205,7 @@ class Film(FlatCAMTool):
|
|||
return 'fail'
|
||||
else:
|
||||
try:
|
||||
if self.units == 'INCH':
|
||||
if self.units == 'IN':
|
||||
unit = inch
|
||||
else:
|
||||
unit = mm
|
||||
|
@ -1198,11 +1213,10 @@ class Film(FlatCAMTool):
|
|||
doc_final = StringIO(doc_final)
|
||||
drawing = svg2rlg(doc_final)
|
||||
|
||||
p_size = self.pagesize_combo.get_value()
|
||||
if p_size == 'Bounds':
|
||||
renderPDF.drawToFile(drawing, filename)
|
||||
else:
|
||||
if self.orientation_radio.get_value() == 'p':
|
||||
if orientation == 'p':
|
||||
page_size = portrait(self.pagesize[p_size])
|
||||
else:
|
||||
page_size = landscape(self.pagesize[p_size])
|
||||
|
@ -1225,7 +1239,8 @@ class Film(FlatCAMTool):
|
|||
|
||||
def job_thread_film(app_obj):
|
||||
try:
|
||||
make_positive_film()
|
||||
make_positive_film(p_size=p_size, orientation=orientation, color=color,
|
||||
transparency_level=transparency_level)
|
||||
except Exception:
|
||||
proc.done()
|
||||
return
|
||||
|
@ -1233,7 +1248,8 @@ class Film(FlatCAMTool):
|
|||
|
||||
self.app.worker_task.emit({'fcn': job_thread_film, 'params': [self]})
|
||||
else:
|
||||
make_positive_film()
|
||||
make_positive_film(p_size=p_size, orientation=orientation, color=color,
|
||||
transparency_level=transparency_level)
|
||||
|
||||
def reset_fields(self):
|
||||
self.tf_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
|
||||
|
|
|
@ -681,6 +681,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
|||
"ppname_g": self.app.defaults["geometry_ppname_g"],
|
||||
"depthperpass": self.app.defaults["geometry_depthperpass"],
|
||||
"extracut": self.app.defaults["geometry_extracut"],
|
||||
"extracut_length": self.app.defaults["geometry_extracut_length"],
|
||||
"toolchange": self.app.defaults["geometry_toolchange"],
|
||||
"toolchangez": self.app.defaults["geometry_toolchangez"],
|
||||
"endz": self.app.defaults["geometry_endz"],
|
||||
|
|
|
@ -440,6 +440,7 @@ class ToolPaint(FlatCAMTool, Gerber):
|
|||
"ppname_g": self.app.defaults["geometry_ppname_g"],
|
||||
"depthperpass": self.app.defaults["geometry_depthperpass"],
|
||||
"extracut": self.app.defaults["geometry_extracut"],
|
||||
"extracut_length": self.app.defaults["geometry_extracut_length"],
|
||||
"toolchange": self.app.defaults["geometry_toolchange"],
|
||||
"toolchangez": self.app.defaults["geometry_toolchangez"],
|
||||
"endz": self.app.defaults["geometry_endz"],
|
||||
|
@ -633,6 +634,7 @@ class ToolPaint(FlatCAMTool, Gerber):
|
|||
"ppname_g": self.app.defaults["geometry_ppname_g"],
|
||||
"depthperpass": float(self.app.defaults["geometry_depthperpass"]),
|
||||
"extracut": self.app.defaults["geometry_extracut"],
|
||||
"extracut_length": self.app.defaults["geometry_extracut_length"],
|
||||
"toolchange": self.app.defaults["geometry_toolchange"],
|
||||
"toolchangez": float(self.app.defaults["geometry_toolchangez"]),
|
||||
"endz": float(self.app.defaults["geometry_endz"]),
|
||||
|
|
|
@ -1283,8 +1283,7 @@ class SolderPaste(FlatCAMTool):
|
|||
obj = self.app.collection.get_by_name(name)
|
||||
|
||||
if name == '':
|
||||
self.app.inform.emit('[WARNING_NOTCL] %s' %
|
||||
_("There is no Geometry object available."))
|
||||
self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Geometry object available."))
|
||||
return 'fail'
|
||||
|
||||
if obj.special_group != 'solder_paste_tool':
|
||||
|
@ -1298,8 +1297,7 @@ class SolderPaste(FlatCAMTool):
|
|||
if obj.tools[tooluid_key]['solid_geometry'] is None:
|
||||
a += 1
|
||||
if a == len(obj.tools):
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s...' %
|
||||
_('Cancelled. Empty file, it has no geometry'))
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s...' % _('Cancelled. Empty file, it has no geometry'))
|
||||
return 'fail'
|
||||
|
||||
# use the name of the first tool selected in self.geo_tools_table which has the diameter passed as tool_dia
|
||||
|
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,11 +1,11 @@
|
|||
# This file contains python only requirements to be installed with pip
|
||||
# Python packages that cannot be installed with pip (e.g. PyQt5, GDAL) are not included.
|
||||
# Usage: pip3 install -r requirements.txt
|
||||
numpy>=1.16
|
||||
numpy >=1.16
|
||||
matplotlib>=3.1
|
||||
cycler>=0.10
|
||||
python-dateutil>=2.1
|
||||
kiwisolver>=1.0.1
|
||||
kiwisolver>=1.1
|
||||
six
|
||||
setuptools
|
||||
dill
|
||||
|
@ -21,6 +21,6 @@ fontTools
|
|||
rasterio
|
||||
lxml
|
||||
ezdxf
|
||||
qrcode>=6.0
|
||||
reportlab>=3.0
|
||||
qrcode>=6.1
|
||||
reportlab>=3.5
|
||||
svglib
|
Binary file not shown.
After Width: | Height: | Size: 242 B |
Binary file not shown.
After Width: | Height: | Size: 381 B |
|
@ -35,6 +35,7 @@ class TclCommandCncjob(TclCommandSignaled):
|
|||
('feedrate_rapid', float),
|
||||
('multidepth', bool),
|
||||
('extracut', bool),
|
||||
('extracut_length', float),
|
||||
('depthperpass', float),
|
||||
('toolchange', int),
|
||||
('toolchangez', float),
|
||||
|
@ -65,6 +66,7 @@ class TclCommandCncjob(TclCommandSignaled):
|
|||
('feedrate_rapid', 'Rapid moving at speed when cutting.'),
|
||||
('multidepth', 'Use or not multidepth cnc cut. (True or False)'),
|
||||
('extracut', 'Use or not an extra cnccut over the first point in path,in the job end (example: True)'),
|
||||
('extracut', 'The value for extra cnccut over the first point in path,in the job end; float'),
|
||||
('depthperpass', 'Height of one layer for multidepth.'),
|
||||
('toolchange', 'Enable tool changes (example: True).'),
|
||||
('toolchangez', 'Z distance for toolchange (example: 30.0).'),
|
||||
|
@ -136,6 +138,8 @@ class TclCommandCncjob(TclCommandSignaled):
|
|||
|
||||
args["multidepth"] = bool(args["multidepth"]) if "multidepth" in args else obj.options["multidepth"]
|
||||
args["extracut"] = bool(args["extracut"]) if "extracut" in args else obj.options["extracut"]
|
||||
args["extracut_length"] = float(args["extracut_length"]) if "extracut_length" in args else \
|
||||
obj.options["extracut_length"]
|
||||
args["depthperpass"] = args["depthperpass"] if "depthperpass" in args and args["depthperpass"] else \
|
||||
obj.options["depthperpass"]
|
||||
|
||||
|
@ -143,7 +147,7 @@ class TclCommandCncjob(TclCommandSignaled):
|
|||
self.app.defaults["geometry_startz"]
|
||||
args["endz"] = args["endz"] if "endz" in args and args["endz"] else obj.options["endz"]
|
||||
|
||||
args["spindlespeed"] = args["spindlespeed"] if "spindlespeed" in args and args["spindlespeed"] else None
|
||||
args["spindlespeed"] = args["spindlespeed"] if "spindlespeed" in args and args["spindlespeed"] != 0 else None
|
||||
args["dwell"] = bool(args["dwell"]) if "dwell" in args else obj.options["dwell"]
|
||||
args["dwelltime"] = args["dwelltime"] if "dwelltime" in args and args["dwelltime"] else obj.options["dwelltime"]
|
||||
|
||||
|
@ -189,6 +193,7 @@ class TclCommandCncjob(TclCommandSignaled):
|
|||
local_tools_dict[tool_uid]['data']['feedrate_rapid'] = args["feedrate_rapid"]
|
||||
local_tools_dict[tool_uid]['data']['multidepth'] = args["multidepth"]
|
||||
local_tools_dict[tool_uid]['data']['extracut'] = args["extracut"]
|
||||
local_tools_dict[tool_uid]['data']['extracut_length'] = args["extracut_length"]
|
||||
local_tools_dict[tool_uid]['data']['depthperpass'] = args["depthperpass"]
|
||||
local_tools_dict[tool_uid]['data']['toolchange'] = args["toolchange"]
|
||||
local_tools_dict[tool_uid]['data']['toolchangez'] = args["toolchangez"]
|
||||
|
|
|
@ -170,6 +170,7 @@ class TclCommandCopperClear(TclCommand):
|
|||
"ppname_g": self.app.defaults["geometry_ppname_g"],
|
||||
"depthperpass": self.app.defaults["geometry_depthperpass"],
|
||||
"extracut": self.app.defaults["geometry_extracut"],
|
||||
"extracut_length": self.app.defaults["geometry_extracut_length"],
|
||||
"toolchange": self.app.defaults["geometry_toolchange"],
|
||||
"toolchangez": self.app.defaults["geometry_toolchangez"],
|
||||
"endz": self.app.defaults["geometry_endz"],
|
||||
|
|
|
@ -159,6 +159,7 @@ class TclCommandPaint(TclCommand):
|
|||
"ppname_g": self.app.defaults["geometry_ppname_g"],
|
||||
"depthperpass": self.app.defaults["geometry_depthperpass"],
|
||||
"extracut": self.app.defaults["geometry_extracut"],
|
||||
"extracut_length": self.app.defaults["geometry_extracut_length"],
|
||||
"toolchange": self.app.defaults["geometry_toolchange"],
|
||||
"toolchangez": self.app.defaults["geometry_toolchangez"],
|
||||
"endz": self.app.defaults["geometry_endz"],
|
||||
|
|
Loading…
Reference in New Issue