@@ -1,5 +1,5 @@
|
|||||||
from PyQt5 import QtGui, QtCore, QtWidgets
|
from PyQt5 import QtGui, QtCore, QtWidgets
|
||||||
from flatcamGUI.GUIElements import FCTable, FCEntry, FCButton, FCDoubleSpinner, FCComboBox, FCCheckBox, FCSpinner, \
|
from AppGUI.GUIElements import FCTable, FCEntry, FCButton, FCDoubleSpinner, FCComboBox, FCCheckBox, FCSpinner, \
|
||||||
FCTree, RadioSet, FCFileSaveDialog
|
FCTree, RadioSet, FCFileSaveDialog
|
||||||
from camlib import to_dict
|
from camlib import to_dict
|
||||||
|
|
||||||
@@ -8,8 +8,10 @@ import json
|
|||||||
|
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import math
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -655,7 +657,7 @@ class ToolsDB(QtWidgets.QWidget):
|
|||||||
l_save=str(self.app.get_last_save_folder()),
|
l_save=str(self.app.get_last_save_folder()),
|
||||||
n=_("Tools_Database"),
|
n=_("Tools_Database"),
|
||||||
date=date),
|
date=date),
|
||||||
filter=filter__)
|
ext_filter=filter__)
|
||||||
|
|
||||||
filename = str(filename)
|
filename = str(filename)
|
||||||
|
|
||||||
@@ -1030,6 +1032,7 @@ class ToolsDB2(QtWidgets.QWidget):
|
|||||||
self.advanced_box.setTitle(_("Advanced Geo Parameters"))
|
self.advanced_box.setTitle(_("Advanced Geo Parameters"))
|
||||||
self.advanced_box.setFixedWidth(250)
|
self.advanced_box.setFixedWidth(250)
|
||||||
|
|
||||||
|
# NCC TOOL BOX
|
||||||
self.ncc_box = QtWidgets.QGroupBox()
|
self.ncc_box = QtWidgets.QGroupBox()
|
||||||
self.ncc_box.setStyleSheet("""
|
self.ncc_box.setStyleSheet("""
|
||||||
QGroupBox
|
QGroupBox
|
||||||
@@ -1042,6 +1045,7 @@ class ToolsDB2(QtWidgets.QWidget):
|
|||||||
self.ncc_box.setTitle(_("NCC Parameters"))
|
self.ncc_box.setTitle(_("NCC Parameters"))
|
||||||
self.ncc_box.setFixedWidth(250)
|
self.ncc_box.setFixedWidth(250)
|
||||||
|
|
||||||
|
# PAINT TOOL BOX
|
||||||
self.paint_box = QtWidgets.QGroupBox()
|
self.paint_box = QtWidgets.QGroupBox()
|
||||||
self.paint_box.setStyleSheet("""
|
self.paint_box.setStyleSheet("""
|
||||||
QGroupBox
|
QGroupBox
|
||||||
@@ -1054,10 +1058,24 @@ class ToolsDB2(QtWidgets.QWidget):
|
|||||||
self.paint_box.setTitle(_("Paint Parameters"))
|
self.paint_box.setTitle(_("Paint Parameters"))
|
||||||
self.paint_box.setFixedWidth(250)
|
self.paint_box.setFixedWidth(250)
|
||||||
|
|
||||||
|
# ISOLATION TOOL BOX
|
||||||
|
self.iso_box = QtWidgets.QGroupBox()
|
||||||
|
self.iso_box.setStyleSheet("""
|
||||||
|
QGroupBox
|
||||||
|
{
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
self.iso_vlay = QtWidgets.QVBoxLayout()
|
||||||
|
self.iso_box.setTitle(_("Isolation Parameters"))
|
||||||
|
self.iso_box.setFixedWidth(250)
|
||||||
|
|
||||||
self.basic_box.setLayout(self.basic_vlay)
|
self.basic_box.setLayout(self.basic_vlay)
|
||||||
self.advanced_box.setLayout(self.advanced_vlay)
|
self.advanced_box.setLayout(self.advanced_vlay)
|
||||||
self.ncc_box.setLayout(self.ncc_vlay)
|
self.ncc_box.setLayout(self.ncc_vlay)
|
||||||
self.paint_box.setLayout(self.paint_vlay)
|
self.paint_box.setLayout(self.paint_vlay)
|
||||||
|
self.iso_box.setLayout(self.iso_vlay)
|
||||||
|
|
||||||
geo_vlay = QtWidgets.QVBoxLayout()
|
geo_vlay = QtWidgets.QVBoxLayout()
|
||||||
geo_vlay.addWidget(self.basic_box)
|
geo_vlay.addWidget(self.basic_box)
|
||||||
@@ -1067,6 +1085,7 @@ class ToolsDB2(QtWidgets.QWidget):
|
|||||||
tools_vlay = QtWidgets.QVBoxLayout()
|
tools_vlay = QtWidgets.QVBoxLayout()
|
||||||
tools_vlay.addWidget(self.ncc_box)
|
tools_vlay.addWidget(self.ncc_box)
|
||||||
tools_vlay.addWidget(self.paint_box)
|
tools_vlay.addWidget(self.paint_box)
|
||||||
|
tools_vlay.addWidget(self.iso_box)
|
||||||
tools_vlay.addStretch()
|
tools_vlay.addStretch()
|
||||||
|
|
||||||
param_hlay.addLayout(geo_vlay)
|
param_hlay.addLayout(geo_vlay)
|
||||||
@@ -1478,7 +1497,7 @@ class ToolsDB2(QtWidgets.QWidget):
|
|||||||
|
|
||||||
self.ncc_method_combo = FCComboBox()
|
self.ncc_method_combo = FCComboBox()
|
||||||
self.ncc_method_combo.addItems(
|
self.ncc_method_combo.addItems(
|
||||||
[_("Standard"), _("Seed"), _("Lines")]
|
[_("Standard"), _("Seed"), _("Lines"), _("Combo")]
|
||||||
)
|
)
|
||||||
self.ncc_method_combo.setObjectName("gdb_n_method")
|
self.ncc_method_combo.setObjectName("gdb_n_method")
|
||||||
|
|
||||||
@@ -1621,6 +1640,101 @@ class ToolsDB2(QtWidgets.QWidget):
|
|||||||
self.grid3.addWidget(self.pathconnect_cb, 10, 0)
|
self.grid3.addWidget(self.pathconnect_cb, 10, 0)
|
||||||
self.grid3.addWidget(self.paintcontour_cb, 10, 1)
|
self.grid3.addWidget(self.paintcontour_cb, 10, 1)
|
||||||
|
|
||||||
|
# ###########################################################################
|
||||||
|
# ############### Paint UI form #############################################
|
||||||
|
# ###########################################################################
|
||||||
|
|
||||||
|
self.grid4 = QtWidgets.QGridLayout()
|
||||||
|
self.iso_vlay.addLayout(self.grid4)
|
||||||
|
self.grid4.setColumnStretch(0, 0)
|
||||||
|
self.grid4.setColumnStretch(1, 1)
|
||||||
|
self.iso_vlay.addStretch()
|
||||||
|
|
||||||
|
# Passes
|
||||||
|
passlabel = QtWidgets.QLabel('%s:' % _('Passes'))
|
||||||
|
passlabel.setToolTip(
|
||||||
|
_("Width of the isolation gap in\n"
|
||||||
|
"number (integer) of tool widths.")
|
||||||
|
)
|
||||||
|
self.passes_entry = FCSpinner()
|
||||||
|
self.passes_entry.set_range(1, 999)
|
||||||
|
self.passes_entry.setObjectName("gdb_i_passes")
|
||||||
|
|
||||||
|
self.grid4.addWidget(passlabel, 0, 0)
|
||||||
|
self.grid4.addWidget(self.passes_entry, 0, 1)
|
||||||
|
|
||||||
|
# Overlap Entry
|
||||||
|
overlabel = QtWidgets.QLabel('%s:' % _('Overlap'))
|
||||||
|
overlabel.setToolTip(
|
||||||
|
_("How much (percentage) of the tool width to overlap each tool pass.")
|
||||||
|
)
|
||||||
|
self.iso_overlap_entry = FCDoubleSpinner(suffix='%')
|
||||||
|
self.iso_overlap_entry.set_precision(self.decimals)
|
||||||
|
self.iso_overlap_entry.setWrapping(True)
|
||||||
|
self.iso_overlap_entry.set_range(0.0000, 99.9999)
|
||||||
|
self.iso_overlap_entry.setSingleStep(0.1)
|
||||||
|
self.iso_overlap_entry.setObjectName("gdb_i_overlap")
|
||||||
|
|
||||||
|
self.grid4.addWidget(overlabel, 2, 0)
|
||||||
|
self.grid4.addWidget(self.iso_overlap_entry, 2, 1)
|
||||||
|
|
||||||
|
# Milling Type Radio Button
|
||||||
|
self.milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type'))
|
||||||
|
self.milling_type_label.setToolTip(
|
||||||
|
_("Milling type when the selected tool is of type: 'iso_op':\n"
|
||||||
|
"- climb / best for precision milling and to reduce tool usage\n"
|
||||||
|
"- conventional / useful when there is no backlash compensation")
|
||||||
|
)
|
||||||
|
|
||||||
|
self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
|
||||||
|
{'label': _('Conventional'), 'value': 'cv'}])
|
||||||
|
self.milling_type_radio.setToolTip(
|
||||||
|
_("Milling type when the selected tool is of type: 'iso_op':\n"
|
||||||
|
"- climb / best for precision milling and to reduce tool usage\n"
|
||||||
|
"- conventional / useful when there is no backlash compensation")
|
||||||
|
)
|
||||||
|
self.milling_type_radio.setObjectName("gdb_i_milling_type")
|
||||||
|
|
||||||
|
self.grid4.addWidget(self.milling_type_label, 4, 0)
|
||||||
|
self.grid4.addWidget(self.milling_type_radio, 4, 1)
|
||||||
|
|
||||||
|
# Follow
|
||||||
|
self.follow_label = QtWidgets.QLabel('%s:' % _('Follow'))
|
||||||
|
self.follow_label.setToolTip(
|
||||||
|
_("Generate a 'Follow' geometry.\n"
|
||||||
|
"This means that it will cut through\n"
|
||||||
|
"the middle of the trace.")
|
||||||
|
)
|
||||||
|
|
||||||
|
self.follow_cb = FCCheckBox()
|
||||||
|
self.follow_cb.setToolTip(_("Generate a 'Follow' geometry.\n"
|
||||||
|
"This means that it will cut through\n"
|
||||||
|
"the middle of the trace."))
|
||||||
|
self.follow_cb.setObjectName("gdb_i_follow")
|
||||||
|
|
||||||
|
self.grid4.addWidget(self.follow_label, 6, 0)
|
||||||
|
self.grid4.addWidget(self.follow_cb, 6, 1)
|
||||||
|
|
||||||
|
# Isolation Type
|
||||||
|
self.iso_type_label = QtWidgets.QLabel('%s:' % _('Isolation Type'))
|
||||||
|
self.iso_type_label.setToolTip(
|
||||||
|
_("Choose how the isolation will be executed:\n"
|
||||||
|
"- 'Full' -> complete isolation of polygons\n"
|
||||||
|
"- 'Ext' -> will isolate only on the outside\n"
|
||||||
|
"- 'Int' -> will isolate only on the inside\n"
|
||||||
|
"'Exterior' isolation is almost always possible\n"
|
||||||
|
"(with the right tool) but 'Interior'\n"
|
||||||
|
"isolation can be done only when there is an opening\n"
|
||||||
|
"inside of the polygon (e.g polygon is a 'doughnut' shape).")
|
||||||
|
)
|
||||||
|
self.iso_type_radio = RadioSet([{'label': _('Full'), 'value': 'full'},
|
||||||
|
{'label': _('Ext'), 'value': 'ext'},
|
||||||
|
{'label': _('Int'), 'value': 'int'}])
|
||||||
|
self.iso_type_radio.setObjectName("gdb_i_iso_type")
|
||||||
|
|
||||||
|
self.grid4.addWidget(self.iso_type_label, 8, 0)
|
||||||
|
self.grid4.addWidget(self.iso_type_radio, 8, 1)
|
||||||
|
|
||||||
# ####################################################################
|
# ####################################################################
|
||||||
# ####################################################################
|
# ####################################################################
|
||||||
# GUI for the lower part of the window
|
# GUI for the lower part of the window
|
||||||
@@ -1743,6 +1857,13 @@ class ToolsDB2(QtWidgets.QWidget):
|
|||||||
"tools_paintmethod": self.paintmethod_combo,
|
"tools_paintmethod": self.paintmethod_combo,
|
||||||
"tools_pathconnect": self.pathconnect_cb,
|
"tools_pathconnect": self.pathconnect_cb,
|
||||||
"tools_paintcontour": self.paintcontour_cb,
|
"tools_paintcontour": self.paintcontour_cb,
|
||||||
|
|
||||||
|
# Isolation
|
||||||
|
"tools_iso_passes": self.passes_entry,
|
||||||
|
"tools_iso_overlap": self.iso_overlap_entry,
|
||||||
|
"tools_iso_milling_type": self.milling_type_radio,
|
||||||
|
"tools_iso_follow": self.follow_cb,
|
||||||
|
"tools_iso_isotype": self.iso_type_radio
|
||||||
}
|
}
|
||||||
|
|
||||||
self.name2option = {
|
self.name2option = {
|
||||||
@@ -1787,6 +1908,13 @@ class ToolsDB2(QtWidgets.QWidget):
|
|||||||
'gdb_p_method': "tools_paintmethod",
|
'gdb_p_method': "tools_paintmethod",
|
||||||
'gdb_p_connect': "tools_pathconnect",
|
'gdb_p_connect': "tools_pathconnect",
|
||||||
'gdb_p_contour': "tools_paintcontour",
|
'gdb_p_contour': "tools_paintcontour",
|
||||||
|
|
||||||
|
# Isolation
|
||||||
|
"gdb_i_passes": "tools_iso_passes",
|
||||||
|
"gdb_i_overlap": "tools_iso_overlap",
|
||||||
|
"gdb_i_milling_type": "tools_iso_milling_type",
|
||||||
|
"gdb_i_follow": "tools_iso_follow",
|
||||||
|
"gdb_i_iso_type": "tools_iso_isotype"
|
||||||
}
|
}
|
||||||
|
|
||||||
self.current_toolid = None
|
self.current_toolid = None
|
||||||
@@ -1939,21 +2067,23 @@ class ToolsDB2(QtWidgets.QWidget):
|
|||||||
if self.db_tool_dict:
|
if self.db_tool_dict:
|
||||||
self.storage_to_form(self.db_tool_dict['1'])
|
self.storage_to_form(self.db_tool_dict['1'])
|
||||||
|
|
||||||
# Enable GUI
|
# Enable AppGUI
|
||||||
self.basic_box.setEnabled(True)
|
self.basic_box.setEnabled(True)
|
||||||
self.advanced_box.setEnabled(True)
|
self.advanced_box.setEnabled(True)
|
||||||
self.ncc_box.setEnabled(True)
|
self.ncc_box.setEnabled(True)
|
||||||
self.paint_box.setEnabled(True)
|
self.paint_box.setEnabled(True)
|
||||||
|
self.iso_box.setEnabled(True)
|
||||||
|
|
||||||
self.tree_widget.setCurrentItem(self.tree_widget.topLevelItem(0))
|
self.tree_widget.setCurrentItem(self.tree_widget.topLevelItem(0))
|
||||||
# self.tree_widget.setFocus()
|
# self.tree_widget.setFocus()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Disable GUI
|
# Disable AppGUI
|
||||||
self.basic_box.setEnabled(False)
|
self.basic_box.setEnabled(False)
|
||||||
self.advanced_box.setEnabled(False)
|
self.advanced_box.setEnabled(False)
|
||||||
self.ncc_box.setEnabled(False)
|
self.ncc_box.setEnabled(False)
|
||||||
self.paint_box.setEnabled(False)
|
self.paint_box.setEnabled(False)
|
||||||
|
self.iso_box.setEnabled(False)
|
||||||
else:
|
else:
|
||||||
self.storage_to_form(self.db_tool_dict[str(self.current_toolid)])
|
self.storage_to_form(self.db_tool_dict[str(self.current_toolid)])
|
||||||
|
|
||||||
@@ -2006,6 +2136,13 @@ class ToolsDB2(QtWidgets.QWidget):
|
|||||||
"tools_paintmethod": self.app.defaults["tools_paintmethod"],
|
"tools_paintmethod": self.app.defaults["tools_paintmethod"],
|
||||||
"tools_pathconnect": self.app.defaults["tools_pathconnect"],
|
"tools_pathconnect": self.app.defaults["tools_pathconnect"],
|
||||||
"tools_paintcontour": self.app.defaults["tools_paintcontour"],
|
"tools_paintcontour": self.app.defaults["tools_paintcontour"],
|
||||||
|
|
||||||
|
# Isolation
|
||||||
|
"tools_iso_passes": int(self.app.defaults["tools_iso_passes"]),
|
||||||
|
"tools_iso_overlap": float(self.app.defaults["tools_iso_overlap"]),
|
||||||
|
"tools_iso_milling_type": self.app.defaults["tools_iso_milling_type"],
|
||||||
|
"tools_iso_follow": self.app.defaults["tools_iso_follow"],
|
||||||
|
"tools_iso_isotype": self.app.defaults["tools_iso_isotype"],
|
||||||
})
|
})
|
||||||
|
|
||||||
dict_elem = {}
|
dict_elem = {}
|
||||||
@@ -2117,7 +2254,7 @@ class ToolsDB2(QtWidgets.QWidget):
|
|||||||
l_save=str(self.app.get_last_save_folder()),
|
l_save=str(self.app.get_last_save_folder()),
|
||||||
n=_("Tools_Database"),
|
n=_("Tools_Database"),
|
||||||
date=date),
|
date=date),
|
||||||
filter=filter__)
|
ext_filter=filter__)
|
||||||
|
|
||||||
filename = str(filename)
|
filename = str(filename)
|
||||||
|
|
||||||
@@ -2218,6 +2355,18 @@ class ToolsDB2(QtWidgets.QWidget):
|
|||||||
self.app.tools_db_changed_flag = False
|
self.app.tools_db_changed_flag = False
|
||||||
self.on_save_tools_db()
|
self.on_save_tools_db()
|
||||||
|
|
||||||
|
def on_calculate_tooldia(self):
|
||||||
|
if self.shape_combo.get_value() == 'V':
|
||||||
|
tip_dia = float(self.vdia_entry.get_value())
|
||||||
|
half_tip_angle = float(self.vangle_entry.get_value()) / 2.0
|
||||||
|
cut_z = float(self.cutz_entry.get_value())
|
||||||
|
cut_z = -cut_z if cut_z < 0 else cut_z
|
||||||
|
|
||||||
|
# calculated tool diameter so the cut_z parameter is obeyed
|
||||||
|
tool_dia = tip_dia + (2 * cut_z * math.tan(math.radians(half_tip_angle)))
|
||||||
|
|
||||||
|
self.dia_entry.set_value(tool_dia)
|
||||||
|
|
||||||
def ui_connect(self):
|
def ui_connect(self):
|
||||||
# make sure that we don't make multiple connections to the widgets
|
# make sure that we don't make multiple connections to the widgets
|
||||||
self.ui_disconnect()
|
self.ui_disconnect()
|
||||||
@@ -2247,12 +2396,40 @@ class ToolsDB2(QtWidgets.QWidget):
|
|||||||
if isinstance(wdg, FCSpinner) or isinstance(wdg, FCDoubleSpinner):
|
if isinstance(wdg, FCSpinner) or isinstance(wdg, FCDoubleSpinner):
|
||||||
wdg.valueChanged.connect(self.update_storage)
|
wdg.valueChanged.connect(self.update_storage)
|
||||||
|
|
||||||
|
# connect the calculate tooldia method to the controls
|
||||||
|
# if the tool shape is 'V' the tool dia will be calculated to obey Cut Z parameter
|
||||||
|
self.shape_combo.currentIndexChanged.connect(self.on_calculate_tooldia)
|
||||||
|
self.cutz_entry.valueChanged.connect(self.on_calculate_tooldia)
|
||||||
|
self.vdia_entry.valueChanged.connect(self.on_calculate_tooldia)
|
||||||
|
self.vangle_entry.valueChanged.connect(self.on_calculate_tooldia)
|
||||||
|
|
||||||
|
|
||||||
def ui_disconnect(self):
|
def ui_disconnect(self):
|
||||||
try:
|
try:
|
||||||
self.name_entry.editingFinished.disconnect(self.update_tree_name)
|
self.name_entry.editingFinished.disconnect(self.update_tree_name)
|
||||||
except (TypeError, AttributeError):
|
except (TypeError, AttributeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.shape_combo.currentIndexChanged.disconnect(self.on_calculate_tooldia)
|
||||||
|
except (TypeError, AttributeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.cutz_entry.valueChanged.disconnect(self.on_calculate_tooldia)
|
||||||
|
except (TypeError, AttributeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.vdia_entry.valueChanged.disconnect(self.on_calculate_tooldia)
|
||||||
|
except (TypeError, AttributeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.vangle_entry.valueChanged.disconnect(self.on_calculate_tooldia)
|
||||||
|
except (TypeError, AttributeError):
|
||||||
|
pass
|
||||||
|
|
||||||
for key in self.form_fields:
|
for key in self.form_fields:
|
||||||
wdg = self.form_fields[key]
|
wdg = self.form_fields[key]
|
||||||
|
|
||||||
@@ -2398,6 +2575,18 @@ class ToolsDB2(QtWidgets.QWidget):
|
|||||||
elif wdg_name == "gdb_p_contour":
|
elif wdg_name == "gdb_p_contour":
|
||||||
self.db_tool_dict[tool_id]['data']['tools_paintcontour'] = val
|
self.db_tool_dict[tool_id]['data']['tools_paintcontour'] = val
|
||||||
|
|
||||||
|
# Isolation Tool
|
||||||
|
elif wdg_name == "gdb_i_passes":
|
||||||
|
self.db_tool_dict[tool_id]['data']['tools_iso_passes'] = val
|
||||||
|
elif wdg_name == "gdb_i_overlap":
|
||||||
|
self.db_tool_dict[tool_id]['data']['tools_iso_overlap'] = val
|
||||||
|
elif wdg_name == "gdb_i_milling_type":
|
||||||
|
self.db_tool_dict[tool_id]['data']['tools_iso_milling_type'] = val
|
||||||
|
elif wdg_name == "gdb_i_follow":
|
||||||
|
self.db_tool_dict[tool_id]['data']['tools_iso_follow'] = val
|
||||||
|
elif wdg_name == "gdb_i_iso_type":
|
||||||
|
self.db_tool_dict[tool_id]['data']['tools_iso_isotype'] = val
|
||||||
|
|
||||||
self.callback_app()
|
self.callback_app()
|
||||||
|
|
||||||
def on_tool_requested_from_app(self):
|
def on_tool_requested_from_app(self):
|
||||||
@@ -9,9 +9,9 @@ from PyQt5 import QtGui, QtCore, QtWidgets
|
|||||||
from PyQt5.QtCore import Qt, QSettings
|
from PyQt5.QtCore import Qt, QSettings
|
||||||
|
|
||||||
from camlib import distance, arc, FlatCAMRTreeStorage
|
from camlib import distance, arc, FlatCAMRTreeStorage
|
||||||
from flatcamGUI.GUIElements import FCEntry, FCComboBox, FCTable, FCDoubleSpinner, RadioSet, FCSpinner
|
from AppGUI.GUIElements import FCEntry, FCComboBox, FCTable, FCDoubleSpinner, RadioSet, FCSpinner
|
||||||
from flatcamEditors.FlatCAMGeoEditor import FCShapeTool, DrawTool, DrawToolShape, DrawToolUtilityShape, FlatCAMGeoEditor
|
from AppEditors.FlatCAMGeoEditor import FCShapeTool, DrawTool, DrawToolShape, DrawToolUtilityShape, FlatCAMGeoEditor
|
||||||
from flatcamParsers.ParseExcellon import Excellon
|
from AppParsers.ParseExcellon import Excellon
|
||||||
|
|
||||||
from shapely.geometry import LineString, LinearRing, MultiLineString, Polygon, MultiPolygon, Point
|
from shapely.geometry import LineString, LinearRing, MultiLineString, Polygon, MultiPolygon, Point
|
||||||
import shapely.affinity as affinity
|
import shapely.affinity as affinity
|
||||||
@@ -26,7 +26,7 @@ import logging
|
|||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -2123,7 +2123,7 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||||||
else:
|
else:
|
||||||
self.tool_shape = self.app.plotcanvas.new_shape_collection(layers=1)
|
self.tool_shape = self.app.plotcanvas.new_shape_collection(layers=1)
|
||||||
else:
|
else:
|
||||||
from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy
|
from AppGUI.PlotCanvasLegacy import ShapeCollectionLegacy
|
||||||
self.shapes = ShapeCollectionLegacy(obj=self, app=self.app, name='shapes_exc_editor')
|
self.shapes = ShapeCollectionLegacy(obj=self, app=self.app, name='shapes_exc_editor')
|
||||||
self.tool_shape = ShapeCollectionLegacy(obj=self, app=self.app, name='tool_shapes_exc_editor')
|
self.tool_shape = ShapeCollectionLegacy(obj=self, app=self.app, name='tool_shapes_exc_editor')
|
||||||
|
|
||||||
@@ -2239,7 +2239,7 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||||||
|
|
||||||
# store the status of the editor so the Delete at object level will not work until the edit is finished
|
# store the status of the editor so the Delete at object level will not work until the edit is finished
|
||||||
self.editor_active = False
|
self.editor_active = False
|
||||||
log.debug("Initialization of the FlatCAM Excellon Editor is finished ...")
|
log.debug("Initialization of the Excellon Editor is finished ...")
|
||||||
|
|
||||||
def pool_recreated(self, pool):
|
def pool_recreated(self, pool):
|
||||||
self.shapes.pool = pool
|
self.shapes.pool = pool
|
||||||
@@ -2312,7 +2312,7 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||||||
tool_dia = float('%.*f' % (self.decimals, v['C']))
|
tool_dia = float('%.*f' % (self.decimals, v['C']))
|
||||||
self.tool2tooldia[int(k)] = tool_dia
|
self.tool2tooldia[int(k)] = tool_dia
|
||||||
|
|
||||||
# Init GUI
|
# Init AppGUI
|
||||||
self.addtool_entry.set_value(float(self.app.defaults['excellon_editor_newdia']))
|
self.addtool_entry.set_value(float(self.app.defaults['excellon_editor_newdia']))
|
||||||
self.drill_array_size_entry.set_value(int(self.app.defaults['excellon_editor_array_size']))
|
self.drill_array_size_entry.set_value(int(self.app.defaults['excellon_editor_array_size']))
|
||||||
self.drill_axis_radio.set_value(self.app.defaults['excellon_editor_lin_dir'])
|
self.drill_axis_radio.set_value(self.app.defaults['excellon_editor_lin_dir'])
|
||||||
@@ -2819,10 +2819,8 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||||||
self.tool_shape.enabled = True
|
self.tool_shape.enabled = True
|
||||||
# self.app.app_cursor.enabled = True
|
# self.app.app_cursor.enabled = True
|
||||||
|
|
||||||
self.app.ui.snap_max_dist_entry.setEnabled(True)
|
|
||||||
self.app.ui.corner_snap_btn.setEnabled(True)
|
|
||||||
self.app.ui.snap_magnet.setVisible(True)
|
|
||||||
self.app.ui.corner_snap_btn.setVisible(True)
|
self.app.ui.corner_snap_btn.setVisible(True)
|
||||||
|
self.app.ui.snap_magnet.setVisible(True)
|
||||||
|
|
||||||
self.app.ui.exc_editor_menu.setDisabled(False)
|
self.app.ui.exc_editor_menu.setDisabled(False)
|
||||||
self.app.ui.exc_editor_menu.menuAction().setVisible(True)
|
self.app.ui.exc_editor_menu.menuAction().setVisible(True)
|
||||||
@@ -2832,12 +2830,11 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||||||
|
|
||||||
self.app.ui.exc_edit_toolbar.setDisabled(False)
|
self.app.ui.exc_edit_toolbar.setDisabled(False)
|
||||||
self.app.ui.exc_edit_toolbar.setVisible(True)
|
self.app.ui.exc_edit_toolbar.setVisible(True)
|
||||||
# self.app.ui.snap_toolbar.setDisabled(False)
|
# self.app.ui.status_toolbar.setDisabled(False)
|
||||||
|
|
||||||
# start with GRID toolbar activated
|
# start with GRID toolbar activated
|
||||||
if self.app.ui.grid_snap_btn.isChecked() is False:
|
if self.app.ui.grid_snap_btn.isChecked() is False:
|
||||||
self.app.ui.grid_snap_btn.trigger()
|
self.app.ui.grid_snap_btn.trigger()
|
||||||
self.app.ui.on_grid_snap_triggered(state=True)
|
|
||||||
|
|
||||||
self.app.ui.popmenu_disable.setVisible(False)
|
self.app.ui.popmenu_disable.setVisible(False)
|
||||||
self.app.ui.cmenu_newmenu.menuAction().setVisible(False)
|
self.app.ui.cmenu_newmenu.menuAction().setVisible(False)
|
||||||
@@ -2869,30 +2866,8 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||||||
self.clear()
|
self.clear()
|
||||||
self.app.ui.exc_edit_toolbar.setDisabled(True)
|
self.app.ui.exc_edit_toolbar.setDisabled(True)
|
||||||
|
|
||||||
settings = QSettings("Open Source", "FlatCAM")
|
|
||||||
if settings.contains("layout"):
|
|
||||||
layout = settings.value('layout', type=str)
|
|
||||||
if layout == 'standard':
|
|
||||||
# self.app.ui.exc_edit_toolbar.setVisible(False)
|
|
||||||
|
|
||||||
self.app.ui.snap_max_dist_entry.setEnabled(False)
|
|
||||||
self.app.ui.corner_snap_btn.setEnabled(False)
|
|
||||||
self.app.ui.snap_magnet.setVisible(False)
|
|
||||||
self.app.ui.corner_snap_btn.setVisible(False)
|
self.app.ui.corner_snap_btn.setVisible(False)
|
||||||
else:
|
|
||||||
# self.app.ui.exc_edit_toolbar.setVisible(True)
|
|
||||||
|
|
||||||
self.app.ui.snap_max_dist_entry.setEnabled(False)
|
|
||||||
self.app.ui.corner_snap_btn.setEnabled(False)
|
|
||||||
self.app.ui.snap_magnet.setVisible(True)
|
|
||||||
self.app.ui.corner_snap_btn.setVisible(True)
|
|
||||||
else:
|
|
||||||
# self.app.ui.exc_edit_toolbar.setVisible(False)
|
|
||||||
|
|
||||||
self.app.ui.snap_max_dist_entry.setEnabled(False)
|
|
||||||
self.app.ui.corner_snap_btn.setEnabled(False)
|
|
||||||
self.app.ui.snap_magnet.setVisible(False)
|
self.app.ui.snap_magnet.setVisible(False)
|
||||||
self.app.ui.corner_snap_btn.setVisible(False)
|
|
||||||
|
|
||||||
# set the Editor Toolbar visibility to what was before entering in the Editor
|
# set the Editor Toolbar visibility to what was before entering in the Editor
|
||||||
self.app.ui.exc_edit_toolbar.setVisible(False) if self.toolbar_old_state is False \
|
self.app.ui.exc_edit_toolbar.setVisible(False) if self.toolbar_old_state is False \
|
||||||
@@ -3068,7 +3043,7 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||||||
|
|
||||||
self.set_ui()
|
self.set_ui()
|
||||||
|
|
||||||
# now that we hava data, create the GUI interface and add it to the Tool Tab
|
# now that we hava data, create the AppGUI interface and add it to the Tool Tab
|
||||||
self.build_ui(first_run=True)
|
self.build_ui(first_run=True)
|
||||||
|
|
||||||
# we activate this after the initial build as we don't need to see the tool been populated
|
# we activate this after the initial build as we don't need to see the tool been populated
|
||||||
@@ -3361,15 +3336,17 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||||||
with self.app.proc_container.new(_("Creating Excellon.")):
|
with self.app.proc_container.new(_("Creating Excellon.")):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
edited_obj = self.app.new_object("excellon", outname, obj_init)
|
edited_obj = self.app.app_obj.new_object("excellon", outname, obj_init)
|
||||||
edited_obj.source_file = self.app.export_excellon(obj_name=edited_obj.options['name'],
|
edited_obj.source_file = self.app.export_excellon(obj_name=edited_obj.options['name'],
|
||||||
local_use=edited_obj,
|
local_use=edited_obj,
|
||||||
filename=None,
|
filename=None,
|
||||||
use_thread=False)
|
use_thread=False)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
self.deactivate()
|
||||||
log.error("Error on Edited object creation: %s" % str(e))
|
log.error("Error on Edited object creation: %s" % str(e))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
self.deactivate()
|
||||||
self.app.inform.emit('[success] %s' % _("Excellon editing finished."))
|
self.app.inform.emit('[success] %s' % _("Excellon editing finished."))
|
||||||
|
|
||||||
def on_tool_select(self, tool):
|
def on_tool_select(self, tool):
|
||||||
@@ -3463,8 +3440,8 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||||||
self.pos = (self.pos[0], self.pos[1])
|
self.pos = (self.pos[0], self.pos[1])
|
||||||
|
|
||||||
if event.button == 1:
|
if event.button == 1:
|
||||||
self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f <b>Dy</b>: "
|
# self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f <b>Dy</b>: "
|
||||||
"%.4f " % (0, 0))
|
# "%.4f " % (0, 0))
|
||||||
|
|
||||||
# Selection with left mouse button
|
# Selection with left mouse button
|
||||||
if self.active_tool is not None and event.button == 1:
|
if self.active_tool is not None and event.button == 1:
|
||||||
@@ -3801,18 +3778,22 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||||||
self.snap_x = x
|
self.snap_x = x
|
||||||
self.snap_y = y
|
self.snap_y = y
|
||||||
|
|
||||||
# update the position label in the infobar since the APP mouse event handlers are disconnected
|
|
||||||
self.app.ui.position_label.setText(" <b>X</b>: %.4f "
|
|
||||||
"<b>Y</b>: %.4f" % (x, y))
|
|
||||||
|
|
||||||
if self.pos is None:
|
if self.pos is None:
|
||||||
self.pos = (0, 0)
|
self.pos = (0, 0)
|
||||||
self.app.dx = x - self.pos[0]
|
self.app.dx = x - self.pos[0]
|
||||||
self.app.dy = y - self.pos[1]
|
self.app.dy = y - self.pos[1]
|
||||||
|
|
||||||
# update the reference position label in the infobar since the APP mouse event handlers are disconnected
|
# # update the position label in the infobar since the APP mouse event handlers are disconnected
|
||||||
self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f <b>Dy</b>: "
|
self.app.ui.position_label.setText(" <b>X</b>: %.4f "
|
||||||
"%.4f " % (self.app.dx, self.app.dy))
|
"<b>Y</b>: %.4f " % (x, y))
|
||||||
|
# # update the reference position label in the infobar since the APP mouse event handlers are disconnected
|
||||||
|
# self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f <b>Dy</b>: "
|
||||||
|
# "%.4f " % (self.app.dx, self.app.dy))
|
||||||
|
|
||||||
|
units = self.app.defaults["units"].lower()
|
||||||
|
self.app.plotcanvas.text_hud.text = \
|
||||||
|
'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\n\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format(
|
||||||
|
self.app.dx, units, self.app.dy, units, x, units, y, units)
|
||||||
|
|
||||||
# ## Utility geometry (animated)
|
# ## Utility geometry (animated)
|
||||||
self.update_utility_geometry(data=(x, y))
|
self.update_utility_geometry(data=(x, y))
|
||||||
@@ -4045,7 +4026,7 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||||||
|
|
||||||
def select_tool(self, toolname):
|
def select_tool(self, toolname):
|
||||||
"""
|
"""
|
||||||
Selects a drawing tool. Impacts the object and GUI.
|
Selects a drawing tool. Impacts the object and AppGUI.
|
||||||
|
|
||||||
:param toolname: Name of the tool.
|
:param toolname: Name of the tool.
|
||||||
:return: None
|
:return: None
|
||||||
@@ -15,11 +15,10 @@ from PyQt5 import QtGui, QtCore, QtWidgets
|
|||||||
from PyQt5.QtCore import Qt, QSettings
|
from PyQt5.QtCore import Qt, QSettings
|
||||||
|
|
||||||
from camlib import distance, arc, three_point_circle, Geometry, FlatCAMRTreeStorage
|
from camlib import distance, arc, three_point_circle, Geometry, FlatCAMRTreeStorage
|
||||||
from FlatCAMTool import FlatCAMTool
|
from AppTool import AppTool
|
||||||
from flatcamGUI.ObjectUI import RadioSet
|
from AppGUI.GUIElements import OptionalInputSection, FCCheckBox, FCEntry, FCComboBox, FCTextAreaRich, \
|
||||||
from flatcamGUI.GUIElements import OptionalInputSection, FCCheckBox, FCEntry, FCComboBox, FCTextAreaRich, \
|
FCDoubleSpinner, FCButton, FCInputDialog, FCTree
|
||||||
FCTable, FCDoubleSpinner, FCButton, EvalEntry2, FCInputDialog, FCTree
|
from AppParsers.ParseFont import *
|
||||||
from flatcamParsers.ParseFont import *
|
|
||||||
|
|
||||||
from shapely.geometry import LineString, LinearRing, MultiLineString, Polygon, MultiPolygon
|
from shapely.geometry import LineString, LinearRing, MultiLineString, Polygon, MultiPolygon
|
||||||
from shapely.ops import cascaded_union, unary_union, linemerge
|
from shapely.ops import cascaded_union, unary_union, linemerge
|
||||||
@@ -34,7 +33,7 @@ from rtree import index as rtindex
|
|||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
# from vispy.io import read_png
|
# from vispy.io import read_png
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -42,7 +41,7 @@ if '_' not in builtins.__dict__:
|
|||||||
_ = gettext.gettext
|
_ = gettext.gettext
|
||||||
|
|
||||||
|
|
||||||
class BufferSelectionTool(FlatCAMTool):
|
class BufferSelectionTool(AppTool):
|
||||||
"""
|
"""
|
||||||
Simple input for buffer distance.
|
Simple input for buffer distance.
|
||||||
"""
|
"""
|
||||||
@@ -50,7 +49,7 @@ class BufferSelectionTool(FlatCAMTool):
|
|||||||
toolName = "Buffer Selection"
|
toolName = "Buffer Selection"
|
||||||
|
|
||||||
def __init__(self, app, draw_app):
|
def __init__(self, app, draw_app):
|
||||||
FlatCAMTool.__init__(self, app)
|
AppTool.__init__(self, app)
|
||||||
|
|
||||||
self.draw_app = draw_app
|
self.draw_app = draw_app
|
||||||
self.decimals = app.decimals
|
self.decimals = app.decimals
|
||||||
@@ -118,12 +117,12 @@ class BufferSelectionTool(FlatCAMTool):
|
|||||||
self.buffer_int_button.clicked.connect(self.on_buffer_int)
|
self.buffer_int_button.clicked.connect(self.on_buffer_int)
|
||||||
self.buffer_ext_button.clicked.connect(self.on_buffer_ext)
|
self.buffer_ext_button.clicked.connect(self.on_buffer_ext)
|
||||||
|
|
||||||
# Init GUI
|
# Init AppGUI
|
||||||
self.buffer_distance_entry.set_value(0.01)
|
self.buffer_distance_entry.set_value(0.01)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.app.defaults.report_usage("Geo Editor ToolBuffer()")
|
self.app.defaults.report_usage("Geo Editor ToolBuffer()")
|
||||||
FlatCAMTool.run(self)
|
AppTool.run(self)
|
||||||
|
|
||||||
# if the splitter us hidden, display it
|
# if the splitter us hidden, display it
|
||||||
if self.app.ui.splitter.sizes()[0] == 0:
|
if self.app.ui.splitter.sizes()[0] == 0:
|
||||||
@@ -187,7 +186,7 @@ class BufferSelectionTool(FlatCAMTool):
|
|||||||
self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
|
self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
|
||||||
|
|
||||||
|
|
||||||
class TextInputTool(FlatCAMTool):
|
class TextInputTool(AppTool):
|
||||||
"""
|
"""
|
||||||
Simple input for buffer distance.
|
Simple input for buffer distance.
|
||||||
"""
|
"""
|
||||||
@@ -195,7 +194,7 @@ class TextInputTool(FlatCAMTool):
|
|||||||
toolName = "Text Input Tool"
|
toolName = "Text Input Tool"
|
||||||
|
|
||||||
def __init__(self, app):
|
def __init__(self, app):
|
||||||
FlatCAMTool.__init__(self, app)
|
AppTool.__init__(self, app)
|
||||||
|
|
||||||
self.app = app
|
self.app = app
|
||||||
self.text_path = []
|
self.text_path = []
|
||||||
@@ -340,7 +339,7 @@ class TextInputTool(FlatCAMTool):
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.app.defaults.report_usage("Geo Editor TextInputTool()")
|
self.app.defaults.report_usage("Geo Editor TextInputTool()")
|
||||||
FlatCAMTool.run(self)
|
AppTool.run(self)
|
||||||
|
|
||||||
# if the splitter us hidden, display it
|
# if the splitter us hidden, display it
|
||||||
if self.app.ui.splitter.sizes()[0] == 0:
|
if self.app.ui.splitter.sizes()[0] == 0:
|
||||||
@@ -405,7 +404,7 @@ class TextInputTool(FlatCAMTool):
|
|||||||
self.app.ui.notebook.setTabText(2, _("Tool"))
|
self.app.ui.notebook.setTabText(2, _("Tool"))
|
||||||
|
|
||||||
|
|
||||||
class PaintOptionsTool(FlatCAMTool):
|
class PaintOptionsTool(AppTool):
|
||||||
"""
|
"""
|
||||||
Inputs to specify how to paint the selected polygons.
|
Inputs to specify how to paint the selected polygons.
|
||||||
"""
|
"""
|
||||||
@@ -413,7 +412,7 @@ class PaintOptionsTool(FlatCAMTool):
|
|||||||
toolName = "Paint Tool"
|
toolName = "Paint Tool"
|
||||||
|
|
||||||
def __init__(self, app, fcdraw):
|
def __init__(self, app, fcdraw):
|
||||||
FlatCAMTool.__init__(self, app)
|
AppTool.__init__(self, app)
|
||||||
|
|
||||||
self.app = app
|
self.app = app
|
||||||
self.fcdraw = fcdraw
|
self.fcdraw = fcdraw
|
||||||
@@ -538,7 +537,7 @@ class PaintOptionsTool(FlatCAMTool):
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.app.defaults.report_usage("Geo Editor ToolPaint()")
|
self.app.defaults.report_usage("Geo Editor ToolPaint()")
|
||||||
FlatCAMTool.run(self)
|
AppTool.run(self)
|
||||||
|
|
||||||
# if the splitter us hidden, display it
|
# if the splitter us hidden, display it
|
||||||
if self.app.ui.splitter.sizes()[0] == 0:
|
if self.app.ui.splitter.sizes()[0] == 0:
|
||||||
@@ -547,7 +546,7 @@ class PaintOptionsTool(FlatCAMTool):
|
|||||||
self.app.ui.notebook.setTabText(2, _("Paint Tool"))
|
self.app.ui.notebook.setTabText(2, _("Paint Tool"))
|
||||||
|
|
||||||
def set_tool_ui(self):
|
def set_tool_ui(self):
|
||||||
# Init GUI
|
# Init AppGUI
|
||||||
if self.app.defaults["tools_painttooldia"]:
|
if self.app.defaults["tools_painttooldia"]:
|
||||||
self.painttooldia_entry.set_value(self.app.defaults["tools_painttooldia"])
|
self.painttooldia_entry.set_value(self.app.defaults["tools_painttooldia"])
|
||||||
else:
|
else:
|
||||||
@@ -599,7 +598,7 @@ class PaintOptionsTool(FlatCAMTool):
|
|||||||
self.app.ui.splitter.setSizes([0, 1])
|
self.app.ui.splitter.setSizes([0, 1])
|
||||||
|
|
||||||
|
|
||||||
class TransformEditorTool(FlatCAMTool):
|
class TransformEditorTool(AppTool):
|
||||||
"""
|
"""
|
||||||
Inputs to specify how to paint the selected polygons.
|
Inputs to specify how to paint the selected polygons.
|
||||||
"""
|
"""
|
||||||
@@ -612,7 +611,7 @@ class TransformEditorTool(FlatCAMTool):
|
|||||||
offsetName = _("Offset")
|
offsetName = _("Offset")
|
||||||
|
|
||||||
def __init__(self, app, draw_app):
|
def __init__(self, app, draw_app):
|
||||||
FlatCAMTool.__init__(self, app)
|
AppTool.__init__(self, app)
|
||||||
|
|
||||||
self.app = app
|
self.app = app
|
||||||
self.draw_app = draw_app
|
self.draw_app = draw_app
|
||||||
@@ -933,7 +932,7 @@ class TransformEditorTool(FlatCAMTool):
|
|||||||
"the 'y' in (x, y) will be used when using Flip on Y.")
|
"the 'y' in (x, y) will be used when using Flip on Y.")
|
||||||
)
|
)
|
||||||
self.flip_ref_label.setFixedWidth(50)
|
self.flip_ref_label.setFixedWidth(50)
|
||||||
self.flip_ref_entry = FCEntry("(0, 0)")
|
self.flip_ref_entry = FCEntry("0, 0")
|
||||||
|
|
||||||
self.flip_ref_button = FCButton()
|
self.flip_ref_button = FCButton()
|
||||||
self.flip_ref_button.set_value(_("Add"))
|
self.flip_ref_button.set_value(_("Add"))
|
||||||
@@ -981,7 +980,7 @@ class TransformEditorTool(FlatCAMTool):
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.app.defaults.report_usage("Geo Editor Transform Tool()")
|
self.app.defaults.report_usage("Geo Editor Transform Tool()")
|
||||||
FlatCAMTool.run(self)
|
AppTool.run(self)
|
||||||
self.set_tool_ui()
|
self.set_tool_ui()
|
||||||
|
|
||||||
# if the splitter us hidden, display it
|
# if the splitter us hidden, display it
|
||||||
@@ -991,7 +990,7 @@ class TransformEditorTool(FlatCAMTool):
|
|||||||
self.app.ui.notebook.setTabText(2, _("Transform Tool"))
|
self.app.ui.notebook.setTabText(2, _("Transform Tool"))
|
||||||
|
|
||||||
def install(self, icon=None, separator=None, **kwargs):
|
def install(self, icon=None, separator=None, **kwargs):
|
||||||
FlatCAMTool.install(self, icon, separator, shortcut='Alt+T', **kwargs)
|
AppTool.install(self, icon, separator, shortcut='Alt+T', **kwargs)
|
||||||
|
|
||||||
def set_tool_ui(self):
|
def set_tool_ui(self):
|
||||||
# Initialize form
|
# Initialize form
|
||||||
@@ -1048,7 +1047,7 @@ class TransformEditorTool(FlatCAMTool):
|
|||||||
if self.app.defaults["tools_transform_mirror_point"]:
|
if self.app.defaults["tools_transform_mirror_point"]:
|
||||||
self.flip_ref_entry.set_value(self.app.defaults["tools_transform_mirror_point"])
|
self.flip_ref_entry.set_value(self.app.defaults["tools_transform_mirror_point"])
|
||||||
else:
|
else:
|
||||||
self.flip_ref_entry.set_value((0, 0))
|
self.flip_ref_entry.set_value("0, 0")
|
||||||
|
|
||||||
def template(self):
|
def template(self):
|
||||||
if not self.draw_app.selected:
|
if not self.draw_app.selected:
|
||||||
@@ -3383,7 +3382,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||||||
self.shapes = self.app.plotcanvas.new_shape_collection(layers=1)
|
self.shapes = self.app.plotcanvas.new_shape_collection(layers=1)
|
||||||
self.tool_shape = self.app.plotcanvas.new_shape_collection(layers=1)
|
self.tool_shape = self.app.plotcanvas.new_shape_collection(layers=1)
|
||||||
else:
|
else:
|
||||||
from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy
|
from AppGUI.PlotCanvasLegacy import ShapeCollectionLegacy
|
||||||
self.shapes = ShapeCollectionLegacy(obj=self, app=self.app, name='shapes_geo_editor')
|
self.shapes = ShapeCollectionLegacy(obj=self, app=self.app, name='shapes_geo_editor')
|
||||||
self.tool_shape = ShapeCollectionLegacy(obj=self, app=self.app, name='tool_shapes_geo_editor')
|
self.tool_shape = ShapeCollectionLegacy(obj=self, app=self.app, name='tool_shapes_geo_editor')
|
||||||
|
|
||||||
@@ -3467,22 +3466,32 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
self.options[opt] = float(entry.text())
|
text_value = entry.text()
|
||||||
|
if ',' in text_value:
|
||||||
|
text_value = text_value.replace(',', '.')
|
||||||
|
self.options[opt] = float(text_value)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
entry.set_value(self.app.defaults[opt])
|
||||||
log.debug("FlatCAMGeoEditor.__init__().entry2option() --> %s" % str(e))
|
log.debug("FlatCAMGeoEditor.__init__().entry2option() --> %s" % str(e))
|
||||||
return
|
return
|
||||||
|
|
||||||
def gridx_changed(goption, gentry):
|
def grid_changed(goption, gentry):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
:param goption: String. Can be either 'global_gridx' or 'global_gridy'
|
:param goption: String. Can be either 'global_gridx' or 'global_gridy'
|
||||||
:param gentry: A GUI element which text value is read and used
|
:param gentry: A GUI element which text value is read and used
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
if goption not in ['global_gridx', 'global_gridy']:
|
||||||
|
return
|
||||||
|
|
||||||
entry2option(opt=goption, entry=gentry)
|
entry2option(opt=goption, entry=gentry)
|
||||||
# if the grid link is checked copy the value in the GridX field to GridY
|
# if the grid link is checked copy the value in the GridX field to GridY
|
||||||
try:
|
try:
|
||||||
val = float(gentry.get_value())
|
text_value = gentry.text()
|
||||||
|
if ',' in text_value:
|
||||||
|
text_value = text_value.replace(',', '.')
|
||||||
|
val = float(text_value)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -3491,7 +3500,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||||||
|
|
||||||
self.app.ui.grid_gap_x_entry.setValidator(QtGui.QDoubleValidator())
|
self.app.ui.grid_gap_x_entry.setValidator(QtGui.QDoubleValidator())
|
||||||
self.app.ui.grid_gap_x_entry.textChanged.connect(
|
self.app.ui.grid_gap_x_entry.textChanged.connect(
|
||||||
lambda: gridx_changed("global_gridx", self.app.ui.grid_gap_x_entry))
|
lambda: grid_changed("global_gridx", self.app.ui.grid_gap_x_entry))
|
||||||
|
|
||||||
self.app.ui.grid_gap_y_entry.setValidator(QtGui.QDoubleValidator())
|
self.app.ui.grid_gap_y_entry.setValidator(QtGui.QDoubleValidator())
|
||||||
self.app.ui.grid_gap_y_entry.textChanged.connect(
|
self.app.ui.grid_gap_y_entry.textChanged.connect(
|
||||||
@@ -3542,7 +3551,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||||||
|
|
||||||
# store the status of the editor so the Delete at object level will not work until the edit is finished
|
# store the status of the editor so the Delete at object level will not work until the edit is finished
|
||||||
self.editor_active = False
|
self.editor_active = False
|
||||||
log.debug("Initialization of the FlatCAM Geometry Editor is finished ...")
|
log.debug("Initialization of the Geometry Editor is finished ...")
|
||||||
|
|
||||||
def pool_recreated(self, pool):
|
def pool_recreated(self, pool):
|
||||||
self.shapes.pool = pool
|
self.shapes.pool = pool
|
||||||
@@ -3559,14 +3568,14 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||||||
|
|
||||||
# Remove anything else in the GUI Selected Tab
|
# Remove anything else in the GUI Selected Tab
|
||||||
self.app.ui.selected_scroll_area.takeWidget()
|
self.app.ui.selected_scroll_area.takeWidget()
|
||||||
# Put ourselves in the GUI Selected Tab
|
# Put ourselves in the AppGUI Selected Tab
|
||||||
self.app.ui.selected_scroll_area.setWidget(self.geo_edit_widget)
|
self.app.ui.selected_scroll_area.setWidget(self.geo_edit_widget)
|
||||||
# Switch notebook to Selected page
|
# Switch notebook to Selected page
|
||||||
self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
|
self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
|
||||||
|
|
||||||
def build_ui(self):
|
def build_ui(self):
|
||||||
"""
|
"""
|
||||||
Build the GUI in the Selected Tab for this editor
|
Build the AppGUI in the Selected Tab for this editor
|
||||||
|
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
@@ -3644,10 +3653,8 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||||||
self.tool_shape.enabled = True
|
self.tool_shape.enabled = True
|
||||||
self.app.app_cursor.enabled = True
|
self.app.app_cursor.enabled = True
|
||||||
|
|
||||||
self.app.ui.snap_max_dist_entry.setEnabled(True)
|
|
||||||
self.app.ui.corner_snap_btn.setEnabled(True)
|
|
||||||
self.app.ui.snap_magnet.setVisible(True)
|
|
||||||
self.app.ui.corner_snap_btn.setVisible(True)
|
self.app.ui.corner_snap_btn.setVisible(True)
|
||||||
|
self.app.ui.snap_magnet.setVisible(True)
|
||||||
|
|
||||||
self.app.ui.geo_editor_menu.setDisabled(False)
|
self.app.ui.geo_editor_menu.setDisabled(False)
|
||||||
self.app.ui.geo_editor_menu.menuAction().setVisible(True)
|
self.app.ui.geo_editor_menu.menuAction().setVisible(True)
|
||||||
@@ -3658,7 +3665,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||||||
self.app.ui.geo_edit_toolbar.setDisabled(False)
|
self.app.ui.geo_edit_toolbar.setDisabled(False)
|
||||||
self.app.ui.geo_edit_toolbar.setVisible(True)
|
self.app.ui.geo_edit_toolbar.setVisible(True)
|
||||||
|
|
||||||
self.app.ui.snap_toolbar.setDisabled(False)
|
self.app.ui.status_toolbar.setDisabled(False)
|
||||||
|
|
||||||
self.app.ui.popmenu_disable.setVisible(False)
|
self.app.ui.popmenu_disable.setVisible(False)
|
||||||
self.app.ui.cmenu_newmenu.menuAction().setVisible(False)
|
self.app.ui.cmenu_newmenu.menuAction().setVisible(False)
|
||||||
@@ -3675,7 +3682,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||||||
|
|
||||||
self.item_selected.connect(self.on_geo_elem_selected)
|
self.item_selected.connect(self.on_geo_elem_selected)
|
||||||
|
|
||||||
# ## GUI Events
|
# ## AppGUI Events
|
||||||
self.tw.itemSelectionChanged.connect(self.on_tree_selection_change)
|
self.tw.itemSelectionChanged.connect(self.on_tree_selection_change)
|
||||||
# self.tw.keyPressed.connect(self.app.ui.keyPressEvent)
|
# self.tw.keyPressed.connect(self.app.ui.keyPressEvent)
|
||||||
# self.tw.customContextMenuRequested.connect(self.on_menu_request)
|
# self.tw.customContextMenuRequested.connect(self.on_menu_request)
|
||||||
@@ -3703,27 +3710,8 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||||||
self.app.ui.geo_edit_toolbar.setDisabled(True)
|
self.app.ui.geo_edit_toolbar.setDisabled(True)
|
||||||
|
|
||||||
settings = QSettings("Open Source", "FlatCAM")
|
settings = QSettings("Open Source", "FlatCAM")
|
||||||
if settings.contains("layout"):
|
|
||||||
layout = settings.value('layout', type=str)
|
|
||||||
if layout == 'standard':
|
|
||||||
# self.app.ui.geo_edit_toolbar.setVisible(False)
|
|
||||||
|
|
||||||
self.app.ui.snap_max_dist_entry.setEnabled(False)
|
|
||||||
self.app.ui.corner_snap_btn.setEnabled(False)
|
|
||||||
self.app.ui.snap_magnet.setVisible(False)
|
|
||||||
self.app.ui.corner_snap_btn.setVisible(False)
|
self.app.ui.corner_snap_btn.setVisible(False)
|
||||||
else:
|
|
||||||
# self.app.ui.geo_edit_toolbar.setVisible(True)
|
|
||||||
|
|
||||||
self.app.ui.snap_max_dist_entry.setEnabled(False)
|
|
||||||
self.app.ui.corner_snap_btn.setEnabled(False)
|
|
||||||
else:
|
|
||||||
# self.app.ui.geo_edit_toolbar.setVisible(False)
|
|
||||||
|
|
||||||
self.app.ui.snap_magnet.setVisible(False)
|
self.app.ui.snap_magnet.setVisible(False)
|
||||||
self.app.ui.corner_snap_btn.setVisible(False)
|
|
||||||
self.app.ui.snap_max_dist_entry.setEnabled(False)
|
|
||||||
self.app.ui.corner_snap_btn.setEnabled(False)
|
|
||||||
|
|
||||||
# set the Editor Toolbar visibility to what was before entering in the Editor
|
# set the Editor Toolbar visibility to what was before entering in the Editor
|
||||||
self.app.ui.geo_edit_toolbar.setVisible(False) if self.toolbar_old_state is False \
|
self.app.ui.geo_edit_toolbar.setVisible(False) if self.toolbar_old_state is False \
|
||||||
@@ -3757,7 +3745,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# ## GUI Events
|
# ## AppGUI Events
|
||||||
self.tw.itemSelectionChanged.disconnect(self.on_tree_selection_change)
|
self.tw.itemSelectionChanged.disconnect(self.on_tree_selection_change)
|
||||||
# self.tw.keyPressed.connect(self.app.ui.keyPressEvent)
|
# self.tw.keyPressed.connect(self.app.ui.keyPressEvent)
|
||||||
# self.tw.customContextMenuRequested.connect(self.on_menu_request)
|
# self.tw.customContextMenuRequested.connect(self.on_menu_request)
|
||||||
@@ -4100,7 +4088,6 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||||||
# start with GRID toolbar activated
|
# start with GRID toolbar activated
|
||||||
if self.app.ui.grid_snap_btn.isChecked() is False:
|
if self.app.ui.grid_snap_btn.isChecked() is False:
|
||||||
self.app.ui.grid_snap_btn.trigger()
|
self.app.ui.grid_snap_btn.trigger()
|
||||||
self.app.ui.on_grid_snap_triggered(state=True)
|
|
||||||
|
|
||||||
def on_buffer_tool(self):
|
def on_buffer_tool(self):
|
||||||
buff_tool = BufferSelectionTool(self.app, self)
|
buff_tool = BufferSelectionTool(self.app, self)
|
||||||
@@ -4148,9 +4135,11 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||||||
|
|
||||||
# make sure that the cursor shape is enabled/disabled, too
|
# make sure that the cursor shape is enabled/disabled, too
|
||||||
if self.options['grid_snap'] is True:
|
if self.options['grid_snap'] is True:
|
||||||
|
self.app.inform[str, bool].emit(_("Grid Snap enabled."), False)
|
||||||
self.app.app_cursor.enabled = True
|
self.app.app_cursor.enabled = True
|
||||||
else:
|
else:
|
||||||
self.app.app_cursor.enabled = False
|
self.app.app_cursor.enabled = False
|
||||||
|
self.app.inform[str, bool].emit(_("Grid Snap disabled."), False)
|
||||||
|
|
||||||
def on_canvas_click(self, event):
|
def on_canvas_click(self, event):
|
||||||
"""
|
"""
|
||||||
@@ -4173,8 +4162,8 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||||||
self.pos = (self.pos[0], self.pos[1])
|
self.pos = (self.pos[0], self.pos[1])
|
||||||
|
|
||||||
if event.button == 1:
|
if event.button == 1:
|
||||||
self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f <b>Dy</b>: "
|
# self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f <b>Dy</b>: "
|
||||||
"%.4f " % (0, 0))
|
# "%.4f " % (0, 0))
|
||||||
|
|
||||||
modifiers = QtWidgets.QApplication.keyboardModifiers()
|
modifiers = QtWidgets.QApplication.keyboardModifiers()
|
||||||
# If the SHIFT key is pressed when LMB is clicked then the coordinates are copied to clipboard
|
# If the SHIFT key is pressed when LMB is clicked then the coordinates are copied to clipboard
|
||||||
@@ -4261,18 +4250,23 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||||||
self.snap_y = y
|
self.snap_y = y
|
||||||
self.app.mouse = [x, y]
|
self.app.mouse = [x, y]
|
||||||
|
|
||||||
# update the position label in the infobar since the APP mouse event handlers are disconnected
|
|
||||||
self.app.ui.position_label.setText(" <b>X</b>: %.4f "
|
|
||||||
"<b>Y</b>: %.4f" % (x, y))
|
|
||||||
|
|
||||||
if self.pos is None:
|
if self.pos is None:
|
||||||
self.pos = (0, 0)
|
self.pos = (0, 0)
|
||||||
self.app.dx = x - self.pos[0]
|
self.app.dx = x - self.pos[0]
|
||||||
self.app.dy = y - self.pos[1]
|
self.app.dy = y - self.pos[1]
|
||||||
|
|
||||||
# update the reference position label in the infobar since the APP mouse event handlers are disconnected
|
# # update the position label in the infobar since the APP mouse event handlers are disconnected
|
||||||
self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f <b>Dy</b>: "
|
self.app.ui.position_label.setText(" <b>X</b>: %.4f "
|
||||||
"%.4f " % (self.app.dx, self.app.dy))
|
"<b>Y</b>: %.4f " % (x, y))
|
||||||
|
#
|
||||||
|
# # update the reference position label in the infobar since the APP mouse event handlers are disconnected
|
||||||
|
# self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f <b>Dy</b>: "
|
||||||
|
# "%.4f " % (self.app.dx, self.app.dy))
|
||||||
|
|
||||||
|
units = self.app.defaults["units"].lower()
|
||||||
|
self.app.plotcanvas.text_hud.text = \
|
||||||
|
'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\n\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format(
|
||||||
|
self.app.dx, units, self.app.dy, units, x, units, y, units)
|
||||||
|
|
||||||
if event.button == 1 and event_is_dragging and isinstance(self.active_tool, FCEraser):
|
if event.button == 1 and event_is_dragging and isinstance(self.active_tool, FCEraser):
|
||||||
pass
|
pass
|
||||||
@@ -4665,7 +4659,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||||||
|
|
||||||
def select_tool(self, toolname):
|
def select_tool(self, toolname):
|
||||||
"""
|
"""
|
||||||
Selects a drawing tool. Impacts the object and GUI.
|
Selects a drawing tool. Impacts the object and AppGUI.
|
||||||
|
|
||||||
:param toolname: Name of the tool.
|
:param toolname: Name of the tool.
|
||||||
:return: None
|
:return: None
|
||||||
@@ -4776,6 +4770,8 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||||||
new_geo = linemerge(new_geo)
|
new_geo = linemerge(new_geo)
|
||||||
fcgeometry.solid_geometry.append(new_geo)
|
fcgeometry.solid_geometry.append(new_geo)
|
||||||
|
|
||||||
|
self.deactivate()
|
||||||
|
|
||||||
def update_options(self, obj):
|
def update_options(self, obj):
|
||||||
if self.paint_tooldia:
|
if self.paint_tooldia:
|
||||||
obj.options['cnctooldia'] = deepcopy(str(self.paint_tooldia))
|
obj.options['cnctooldia'] = deepcopy(str(self.paint_tooldia))
|
||||||
@@ -14,15 +14,13 @@ import shapely.affinity as affinity
|
|||||||
|
|
||||||
from vispy.geometry import Rect
|
from vispy.geometry import Rect
|
||||||
|
|
||||||
import threading
|
|
||||||
import time
|
|
||||||
from copy import copy, deepcopy
|
from copy import copy, deepcopy
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from camlib import distance, arc, three_point_circle
|
from camlib import distance, arc, three_point_circle
|
||||||
from flatcamGUI.GUIElements import FCEntry, FCComboBox, FCTable, FCDoubleSpinner, FCSpinner, RadioSet, \
|
from AppGUI.GUIElements import FCEntry, FCComboBox, FCTable, FCDoubleSpinner, FCSpinner, RadioSet, \
|
||||||
EvalEntry2, FCInputDialog, FCButton, OptionalInputSection, FCCheckBox
|
EvalEntry2, FCInputDialog, FCButton, OptionalInputSection, FCCheckBox
|
||||||
from FlatCAMTool import FlatCAMTool
|
from AppTool import AppTool
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from numpy.linalg import norm as numpy_norm
|
from numpy.linalg import norm as numpy_norm
|
||||||
@@ -32,7 +30,7 @@ import math
|
|||||||
# import pngcanvas
|
# import pngcanvas
|
||||||
import traceback
|
import traceback
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -1086,15 +1084,6 @@ class FCRegion(FCShapeTool):
|
|||||||
|
|
||||||
self.draw_app.app.inform.emit('[success] %s' % _("Done."))
|
self.draw_app.app.inform.emit('[success] %s' % _("Done."))
|
||||||
|
|
||||||
def clean_up(self):
|
|
||||||
self.draw_app.selected = []
|
|
||||||
self.draw_app.apertures_table.clearSelection()
|
|
||||||
self.draw_app.plot_all()
|
|
||||||
try:
|
|
||||||
self.draw_app.app.jump_signal.disconnect()
|
|
||||||
except (TypeError, AttributeError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_key(self, key):
|
def on_key(self, key):
|
||||||
# Jump to coords
|
# Jump to coords
|
||||||
if key == QtCore.Qt.Key_J or key == 'J':
|
if key == QtCore.Qt.Key_J or key == 'J':
|
||||||
@@ -1160,16 +1149,36 @@ class FCRegion(FCShapeTool):
|
|||||||
|
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
def clean_up(self):
|
||||||
|
self.draw_app.selected = []
|
||||||
|
self.draw_app.apertures_table.clearSelection()
|
||||||
|
self.draw_app.plot_all()
|
||||||
|
try:
|
||||||
|
self.draw_app.app.jump_signal.disconnect()
|
||||||
|
except (TypeError, AttributeError):
|
||||||
|
pass
|
||||||
|
|
||||||
class FCTrack(FCRegion):
|
|
||||||
|
class FCTrack(FCShapeTool):
|
||||||
"""
|
"""
|
||||||
Resulting type: Polygon
|
Resulting type: Polygon
|
||||||
"""
|
"""
|
||||||
def __init__(self, draw_app):
|
def __init__(self, draw_app):
|
||||||
FCRegion.__init__(self, draw_app)
|
DrawTool.__init__(self, draw_app)
|
||||||
self.name = 'track'
|
self.name = 'track'
|
||||||
self.draw_app = draw_app
|
self.draw_app = draw_app
|
||||||
|
|
||||||
|
self.steps_per_circle = self.draw_app.app.defaults["gerber_circle_steps"]
|
||||||
|
|
||||||
|
size_ap = float(self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['size'])
|
||||||
|
self.buf_val = (size_ap / 2) if size_ap > 0 else 0.0000001
|
||||||
|
|
||||||
|
self.gridx_size = float(self.draw_app.app.ui.grid_gap_x_entry.get_value())
|
||||||
|
self.gridy_size = float(self.draw_app.app.ui.grid_gap_y_entry.get_value())
|
||||||
|
|
||||||
|
self.temp_points = []
|
||||||
|
|
||||||
|
self. final_click = False
|
||||||
try:
|
try:
|
||||||
QtGui.QGuiApplication.restoreOverrideCursor()
|
QtGui.QGuiApplication.restoreOverrideCursor()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -1183,52 +1192,23 @@ class FCTrack(FCRegion):
|
|||||||
|
|
||||||
self.draw_app.app.inform.emit(_('Track Mode 1: 45 degrees ...'))
|
self.draw_app.app.inform.emit(_('Track Mode 1: 45 degrees ...'))
|
||||||
|
|
||||||
def make(self):
|
|
||||||
new_geo_el = {}
|
|
||||||
if len(self.temp_points) == 1:
|
|
||||||
new_geo_el['solid'] = Point(self.temp_points).buffer(self.buf_val,
|
|
||||||
resolution=int(self.steps_per_circle / 4))
|
|
||||||
new_geo_el['follow'] = Point(self.temp_points)
|
|
||||||
else:
|
|
||||||
new_geo_el['solid'] = (LineString(self.temp_points).buffer(
|
|
||||||
self.buf_val, resolution=int(self.steps_per_circle / 4))).buffer(0)
|
|
||||||
new_geo_el['follow'] = LineString(self.temp_points)
|
|
||||||
|
|
||||||
self.geometry = DrawToolShape(new_geo_el)
|
|
||||||
|
|
||||||
self.draw_app.in_action = False
|
|
||||||
self.complete = True
|
|
||||||
|
|
||||||
self.draw_app.app.jump_signal.disconnect()
|
|
||||||
|
|
||||||
self.draw_app.app.inform.emit('[success] %s' % _("Done."))
|
|
||||||
|
|
||||||
def clean_up(self):
|
|
||||||
self.draw_app.selected = []
|
|
||||||
self.draw_app.apertures_table.clearSelection()
|
|
||||||
self.draw_app.plot_all()
|
|
||||||
try:
|
|
||||||
self.draw_app.app.jump_signal.disconnect()
|
|
||||||
except (TypeError, AttributeError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def click(self, point):
|
def click(self, point):
|
||||||
self.draw_app.in_action = True
|
self.draw_app.in_action = True
|
||||||
try:
|
|
||||||
if point != self.points[-1]:
|
if not self.points:
|
||||||
self.points.append(point)
|
self.points.append(point)
|
||||||
except IndexError:
|
elif point != self.points[-1]:
|
||||||
self.points.append(point)
|
self.points.append(point)
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
new_geo_el = {}
|
new_geo_el = {}
|
||||||
|
|
||||||
if len(self.temp_points) == 1:
|
if len(self.temp_points) == 1:
|
||||||
new_geo_el['solid'] = Point(self.temp_points).buffer(self.buf_val,
|
new_geo_el['solid'] = Point(self.temp_points).buffer(self.buf_val, int(self.steps_per_circle))
|
||||||
resolution=int(self.steps_per_circle / 4))
|
|
||||||
new_geo_el['follow'] = Point(self.temp_points)
|
new_geo_el['follow'] = Point(self.temp_points)
|
||||||
else:
|
else:
|
||||||
new_geo_el['solid'] = LineString(self.temp_points).buffer(self.buf_val,
|
new_geo_el['solid'] = LineString(self.temp_points).buffer(self.buf_val, int(self.steps_per_circle))
|
||||||
resolution=int(self.steps_per_circle / 4))
|
|
||||||
new_geo_el['follow'] = LineString(self.temp_points)
|
new_geo_el['follow'] = LineString(self.temp_points)
|
||||||
|
|
||||||
self.draw_app.add_gerber_shape(DrawToolShape(new_geo_el),
|
self.draw_app.add_gerber_shape(DrawToolShape(new_geo_el),
|
||||||
@@ -1241,23 +1221,25 @@ class FCTrack(FCRegion):
|
|||||||
|
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
def update_grid_info(self):
|
||||||
|
self.gridx_size = float(self.draw_app.app.ui.grid_gap_x_entry.get_value())
|
||||||
|
self.gridy_size = float(self.draw_app.app.ui.grid_gap_y_entry.get_value())
|
||||||
|
|
||||||
def utility_geometry(self, data=None):
|
def utility_geometry(self, data=None):
|
||||||
self.update_grid_info()
|
self.update_grid_info()
|
||||||
new_geo_el = {}
|
new_geo_el = {}
|
||||||
|
|
||||||
if len(self.points) == 0:
|
if not self.points:
|
||||||
new_geo_el['solid'] = Point(data).buffer(self.buf_val,
|
new_geo_el['solid'] = Point(data).buffer(self.buf_val, int(self.steps_per_circle))
|
||||||
resolution=int(self.steps_per_circle / 4))
|
|
||||||
|
|
||||||
return DrawToolUtilityShape(new_geo_el)
|
return DrawToolUtilityShape(new_geo_el)
|
||||||
elif len(self.points) > 0:
|
else:
|
||||||
|
|
||||||
self.temp_points = [self.points[-1]]
|
|
||||||
old_x = self.points[-1][0]
|
old_x = self.points[-1][0]
|
||||||
old_y = self.points[-1][1]
|
old_y = self.points[-1][1]
|
||||||
x = data[0]
|
x = data[0]
|
||||||
y = data[1]
|
y = data[1]
|
||||||
|
|
||||||
|
self.temp_points = [self.points[-1]]
|
||||||
|
|
||||||
mx = abs(round((x - old_x) / self.gridx_size))
|
mx = abs(round((x - old_x) / self.gridx_size))
|
||||||
my = abs(round((y - old_y) / self.gridy_size))
|
my = abs(round((y - old_y) / self.gridy_size))
|
||||||
|
|
||||||
@@ -1305,14 +1287,30 @@ class FCTrack(FCRegion):
|
|||||||
|
|
||||||
self.temp_points.append(data)
|
self.temp_points.append(data)
|
||||||
if len(self.temp_points) == 1:
|
if len(self.temp_points) == 1:
|
||||||
new_geo_el['solid'] = Point(self.temp_points).buffer(self.buf_val,
|
new_geo_el['solid'] = Point(self.temp_points).buffer(self.buf_val, int(self.steps_per_circle))
|
||||||
resolution=int(self.steps_per_circle / 4))
|
|
||||||
return DrawToolUtilityShape(new_geo_el)
|
return DrawToolUtilityShape(new_geo_el)
|
||||||
|
|
||||||
new_geo_el['solid'] = LineString(self.temp_points).buffer(self.buf_val,
|
new_geo_el['solid'] = LineString(self.temp_points).buffer(self.buf_val, int(self.steps_per_circle))
|
||||||
resolution=int(self.steps_per_circle / 4))
|
|
||||||
return DrawToolUtilityShape(new_geo_el)
|
return DrawToolUtilityShape(new_geo_el)
|
||||||
|
|
||||||
|
def make(self):
|
||||||
|
new_geo_el = {}
|
||||||
|
if len(self.temp_points) == 1:
|
||||||
|
new_geo_el['solid'] = Point(self.temp_points).buffer(self.buf_val, int(self.steps_per_circle))
|
||||||
|
new_geo_el['follow'] = Point(self.temp_points)
|
||||||
|
else:
|
||||||
|
new_geo_el['solid'] = LineString(self.temp_points).buffer(self.buf_val, int(self.steps_per_circle))
|
||||||
|
new_geo_el['solid'] = new_geo_el['solid'].buffer(0) # try to clean the geometry
|
||||||
|
new_geo_el['follow'] = LineString(self.temp_points)
|
||||||
|
|
||||||
|
self.geometry = DrawToolShape(new_geo_el)
|
||||||
|
|
||||||
|
self.draw_app.in_action = False
|
||||||
|
self.complete = True
|
||||||
|
|
||||||
|
self.draw_app.app.jump_signal.disconnect()
|
||||||
|
self.draw_app.app.inform.emit('[success] %s' % _("Done."))
|
||||||
|
|
||||||
def on_key(self, key):
|
def on_key(self, key):
|
||||||
if key == 'Backspace' or key == QtCore.Qt.Key_Backspace:
|
if key == 'Backspace' or key == QtCore.Qt.Key_Backspace:
|
||||||
if len(self.points) > 0:
|
if len(self.points) > 0:
|
||||||
@@ -1405,6 +1403,15 @@ class FCTrack(FCRegion):
|
|||||||
|
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
def clean_up(self):
|
||||||
|
self.draw_app.selected = []
|
||||||
|
self.draw_app.apertures_table.clearSelection()
|
||||||
|
self.draw_app.plot_all()
|
||||||
|
try:
|
||||||
|
self.draw_app.app.jump_signal.disconnect()
|
||||||
|
except (TypeError, AttributeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class FCDisc(FCShapeTool):
|
class FCDisc(FCShapeTool):
|
||||||
"""
|
"""
|
||||||
@@ -2955,7 +2962,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||||||
# this var will store the state of the toolbar before starting the editor
|
# this var will store the state of the toolbar before starting the editor
|
||||||
self.toolbar_old_state = False
|
self.toolbar_old_state = False
|
||||||
|
|
||||||
# Init GUI
|
# Init AppGUI
|
||||||
self.apdim_lbl.hide()
|
self.apdim_lbl.hide()
|
||||||
self.apdim_entry.hide()
|
self.apdim_entry.hide()
|
||||||
self.gerber_obj = None
|
self.gerber_obj = None
|
||||||
@@ -2967,7 +2974,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||||||
self.tool_shape = self.canvas.new_shape_collection(layers=1)
|
self.tool_shape = self.canvas.new_shape_collection(layers=1)
|
||||||
self.ma_annotation = self.canvas.new_text_group()
|
self.ma_annotation = self.canvas.new_text_group()
|
||||||
else:
|
else:
|
||||||
from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy
|
from AppGUI.PlotCanvasLegacy import ShapeCollectionLegacy
|
||||||
self.shapes = ShapeCollectionLegacy(obj=self, app=self.app, name='shapes_grb_editor')
|
self.shapes = ShapeCollectionLegacy(obj=self, app=self.app, name='shapes_grb_editor')
|
||||||
self.tool_shape = ShapeCollectionLegacy(obj=self, app=self.app, name='tool_shapes_grb_editor')
|
self.tool_shape = ShapeCollectionLegacy(obj=self, app=self.app, name='tool_shapes_grb_editor')
|
||||||
self.ma_annotation = ShapeCollectionLegacy(
|
self.ma_annotation = ShapeCollectionLegacy(
|
||||||
@@ -3110,7 +3117,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||||||
self.complete = True
|
self.complete = True
|
||||||
|
|
||||||
self.set_ui()
|
self.set_ui()
|
||||||
log.debug("Initialization of the FlatCAM Gerber Editor is finished ...")
|
log.debug("Initialization of the Gerber Editor is finished ...")
|
||||||
|
|
||||||
def pool_recreated(self, pool):
|
def pool_recreated(self, pool):
|
||||||
self.shapes.pool = pool
|
self.shapes.pool = pool
|
||||||
@@ -3139,7 +3146,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||||||
tt_aperture = self.sorted_apcode[i]
|
tt_aperture = self.sorted_apcode[i]
|
||||||
self.tid2apcode[i + 1] = tt_aperture
|
self.tid2apcode[i + 1] = tt_aperture
|
||||||
|
|
||||||
# Init GUI
|
# Init AppGUI
|
||||||
|
|
||||||
self.buffer_distance_entry.set_value(self.app.defaults["gerber_editor_buff_f"])
|
self.buffer_distance_entry.set_value(self.app.defaults["gerber_editor_buff_f"])
|
||||||
self.scale_factor_entry.set_value(self.app.defaults["gerber_editor_scale_f"])
|
self.scale_factor_entry.set_value(self.app.defaults["gerber_editor_scale_f"])
|
||||||
@@ -3685,10 +3692,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||||||
self.shapes.enabled = True
|
self.shapes.enabled = True
|
||||||
self.tool_shape.enabled = True
|
self.tool_shape.enabled = True
|
||||||
|
|
||||||
self.app.ui.snap_max_dist_entry.setEnabled(True)
|
|
||||||
self.app.ui.corner_snap_btn.setEnabled(True)
|
|
||||||
self.app.ui.snap_magnet.setVisible(True)
|
|
||||||
self.app.ui.corner_snap_btn.setVisible(True)
|
self.app.ui.corner_snap_btn.setVisible(True)
|
||||||
|
self.app.ui.snap_magnet.setVisible(True)
|
||||||
|
|
||||||
self.app.ui.grb_editor_menu.setDisabled(False)
|
self.app.ui.grb_editor_menu.setDisabled(False)
|
||||||
self.app.ui.grb_editor_menu.menuAction().setVisible(True)
|
self.app.ui.grb_editor_menu.menuAction().setVisible(True)
|
||||||
@@ -3698,12 +3703,11 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||||||
|
|
||||||
self.app.ui.grb_edit_toolbar.setDisabled(False)
|
self.app.ui.grb_edit_toolbar.setDisabled(False)
|
||||||
self.app.ui.grb_edit_toolbar.setVisible(True)
|
self.app.ui.grb_edit_toolbar.setVisible(True)
|
||||||
# self.app.ui.snap_toolbar.setDisabled(False)
|
# self.app.ui.status_toolbar.setDisabled(False)
|
||||||
|
|
||||||
# start with GRID toolbar activated
|
# start with GRID toolbar activated
|
||||||
if self.app.ui.grid_snap_btn.isChecked() is False:
|
if self.app.ui.grid_snap_btn.isChecked() is False:
|
||||||
self.app.ui.grid_snap_btn.trigger()
|
self.app.ui.grid_snap_btn.trigger()
|
||||||
self.app.ui.on_grid_snap_triggered(state=True)
|
|
||||||
|
|
||||||
# adjust the visibility of some of the canvas context menu
|
# adjust the visibility of some of the canvas context menu
|
||||||
self.app.ui.popmenu_edit.setVisible(False)
|
self.app.ui.popmenu_edit.setVisible(False)
|
||||||
@@ -3736,29 +3740,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||||||
self.app.ui.grb_edit_toolbar.setDisabled(True)
|
self.app.ui.grb_edit_toolbar.setDisabled(True)
|
||||||
|
|
||||||
settings = QSettings("Open Source", "FlatCAM")
|
settings = QSettings("Open Source", "FlatCAM")
|
||||||
if settings.contains("layout"):
|
|
||||||
layout = settings.value('layout', type=str)
|
|
||||||
if layout == 'standard':
|
|
||||||
# self.app.ui.exc_edit_toolbar.setVisible(False)
|
|
||||||
|
|
||||||
self.app.ui.snap_max_dist_entry.setEnabled(False)
|
|
||||||
self.app.ui.corner_snap_btn.setEnabled(False)
|
|
||||||
self.app.ui.snap_magnet.setVisible(False)
|
|
||||||
self.app.ui.corner_snap_btn.setVisible(False)
|
self.app.ui.corner_snap_btn.setVisible(False)
|
||||||
else:
|
|
||||||
# self.app.ui.exc_edit_toolbar.setVisible(True)
|
|
||||||
|
|
||||||
self.app.ui.snap_max_dist_entry.setEnabled(False)
|
|
||||||
self.app.ui.corner_snap_btn.setEnabled(False)
|
|
||||||
self.app.ui.snap_magnet.setVisible(True)
|
|
||||||
self.app.ui.corner_snap_btn.setVisible(True)
|
|
||||||
else:
|
|
||||||
# self.app.ui.exc_edit_toolbar.setVisible(False)
|
|
||||||
|
|
||||||
self.app.ui.snap_max_dist_entry.setEnabled(False)
|
|
||||||
self.app.ui.corner_snap_btn.setEnabled(False)
|
|
||||||
self.app.ui.snap_magnet.setVisible(False)
|
self.app.ui.snap_magnet.setVisible(False)
|
||||||
self.app.ui.corner_snap_btn.setVisible(False)
|
|
||||||
|
|
||||||
# set the Editor Toolbar visibility to what was before entering in the Editor
|
# set the Editor Toolbar visibility to what was before entering in the Editor
|
||||||
self.app.ui.grb_edit_toolbar.setVisible(False) if self.toolbar_old_state is False \
|
self.app.ui.grb_edit_toolbar.setVisible(False) if self.toolbar_old_state is False \
|
||||||
@@ -4210,7 +4193,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||||||
|
|
||||||
def on_multiprocessing_finished(self):
|
def on_multiprocessing_finished(self):
|
||||||
self.app.proc_container.update_view_text(' %s' % _("Setting up the UI"))
|
self.app.proc_container.update_view_text(' %s' % _("Setting up the UI"))
|
||||||
self.app.inform.emit('[success] %s.' % _("Adding geometry finished. Preparing the GUI"))
|
self.app.inform.emit('[success] %s.' % _("Adding geometry finished. Preparing the AppGUI"))
|
||||||
self.set_ui()
|
self.set_ui()
|
||||||
self.build_ui(first_run=True)
|
self.build_ui(first_run=True)
|
||||||
self.plot_all()
|
self.plot_all()
|
||||||
@@ -4245,6 +4228,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||||||
new_grb_name = self.edited_obj_name + "_edit"
|
new_grb_name = self.edited_obj_name + "_edit"
|
||||||
|
|
||||||
self.app.worker_task.emit({'fcn': self.new_edited_gerber, 'params': [new_grb_name, self.storage_dict]})
|
self.app.worker_task.emit({'fcn': self.new_edited_gerber, 'params': [new_grb_name, self.storage_dict]})
|
||||||
|
# self.new_edited_gerber(new_grb_name, self.storage_dict)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def update_options(obj):
|
def update_options(obj):
|
||||||
@@ -4269,6 +4253,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||||||
:param outname: Name of the resulting object. None causes the name to be that of the file.
|
:param outname: Name of the resulting object. None causes the name to be that of the file.
|
||||||
:type outname: str
|
:type outname: str
|
||||||
:param aperture_storage: a dictionary that holds all the objects geometry
|
:param aperture_storage: a dictionary that holds all the objects geometry
|
||||||
|
:type aperture_storage: dict
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -4360,26 +4345,27 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
||||||
_("There are no Aperture definitions in the file. Aborting Gerber creation."))
|
_("There are no Aperture definitions in the file. Aborting Gerber creation."))
|
||||||
except Exception:
|
except Exception:
|
||||||
msg = '[ERROR] %s' % \
|
msg = '[ERROR] %s' % _("An internal error has occurred. See shell.\n")
|
||||||
_("An internal error has occurred. See shell.\n")
|
|
||||||
msg += traceback.format_exc()
|
msg += traceback.format_exc()
|
||||||
app_obj.inform.emit(msg)
|
app_obj.inform.emit(msg)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
grb_obj.source_file = self.app.export_gerber(obj_name=out_name, filename=None,
|
grb_obj.source_file = self.app.export_gerber(obj_name=out_name, filename=None,
|
||||||
local_use=grb_obj, use_thread=False)
|
local_use=grb_obj, use_thread=False)
|
||||||
|
|
||||||
with self.app.proc_container.new(_("Creating Gerber.")):
|
with self.app.proc_container.new(_("Creating Gerber.")):
|
||||||
try:
|
try:
|
||||||
self.app.new_object("gerber", outname, obj_init)
|
self.app.app_obj.new_object("gerber", outname, obj_init)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.error("Error on Edited object creation: %s" % str(e))
|
log.error("Error on Edited object creation: %s" % str(e))
|
||||||
# make sure to clean the previous results
|
# make sure to clean the previous results
|
||||||
self.results = []
|
self.results = []
|
||||||
return
|
return
|
||||||
|
|
||||||
self.app.inform.emit('[success] %s' % _("Done. Gerber editing finished."))
|
|
||||||
# make sure to clean the previous results
|
# make sure to clean the previous results
|
||||||
self.results = []
|
self.results = []
|
||||||
|
self.deactivate_grb_editor()
|
||||||
|
self.app.inform.emit('[success] %s' % _("Done. Gerber editing finished."))
|
||||||
|
|
||||||
def on_tool_select(self, tool):
|
def on_tool_select(self, tool):
|
||||||
"""
|
"""
|
||||||
@@ -4537,8 +4523,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||||||
self.pos = (self.pos[0], self.pos[1])
|
self.pos = (self.pos[0], self.pos[1])
|
||||||
|
|
||||||
if event.button == 1:
|
if event.button == 1:
|
||||||
self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f <b>Dy</b>: "
|
# self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f <b>Dy</b>: "
|
||||||
"%.4f " % (0, 0))
|
# "%.4f " % (0, 0))
|
||||||
|
|
||||||
# Selection with left mouse button
|
# Selection with left mouse button
|
||||||
if self.active_tool is not None:
|
if self.active_tool is not None:
|
||||||
@@ -4550,8 +4536,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||||||
self.app.defaults["global_point_clipboard_format"] %
|
self.app.defaults["global_point_clipboard_format"] %
|
||||||
(self.decimals, self.pos[0], self.decimals, self.pos[1])
|
(self.decimals, self.pos[0], self.decimals, self.pos[1])
|
||||||
)
|
)
|
||||||
self.app.inform.emit('[success] %s' %
|
self.app.inform.emit('[success] %s' % _("Coordinates copied to clipboard."))
|
||||||
_("Coordinates copied to clipboard."))
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# Dispatch event to active_tool
|
# Dispatch event to active_tool
|
||||||
@@ -4562,6 +4547,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||||||
if self.current_storage is not None:
|
if self.current_storage is not None:
|
||||||
self.on_grb_shape_complete(self.current_storage)
|
self.on_grb_shape_complete(self.current_storage)
|
||||||
self.build_ui()
|
self.build_ui()
|
||||||
|
|
||||||
# MS: always return to the Select Tool if modifier key is not pressed
|
# MS: always return to the Select Tool if modifier key is not pressed
|
||||||
# else return to the current tool
|
# else return to the current tool
|
||||||
key_modifier = QtWidgets.QApplication.keyboardModifiers()
|
key_modifier = QtWidgets.QApplication.keyboardModifiers()
|
||||||
@@ -4569,6 +4555,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||||||
modifier_to_use = Qt.ControlModifier
|
modifier_to_use = Qt.ControlModifier
|
||||||
else:
|
else:
|
||||||
modifier_to_use = Qt.ShiftModifier
|
modifier_to_use = Qt.ShiftModifier
|
||||||
|
|
||||||
# if modifier key is pressed then we add to the selected list the current shape but if it's already
|
# if modifier key is pressed then we add to the selected list the current shape but if it's already
|
||||||
# in the selected list, we removed it. Therefore first click selects, second deselects.
|
# in the selected list, we removed it. Therefore first click selects, second deselects.
|
||||||
if key_modifier == modifier_to_use:
|
if key_modifier == modifier_to_use:
|
||||||
@@ -4629,12 +4616,14 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||||||
# if right click on canvas and the active tool need to be finished (like Path or Polygon)
|
# if right click on canvas and the active tool need to be finished (like Path or Polygon)
|
||||||
# right mouse click will finish the action
|
# right mouse click will finish the action
|
||||||
if isinstance(self.active_tool, FCShapeTool):
|
if isinstance(self.active_tool, FCShapeTool):
|
||||||
|
if isinstance(self.active_tool, FCTrack):
|
||||||
|
self.active_tool.make()
|
||||||
|
else:
|
||||||
self.active_tool.click(self.app.geo_editor.snap(self.x, self.y))
|
self.active_tool.click(self.app.geo_editor.snap(self.x, self.y))
|
||||||
self.active_tool.make()
|
self.active_tool.make()
|
||||||
if self.active_tool.complete:
|
if self.active_tool.complete:
|
||||||
self.on_grb_shape_complete()
|
self.on_grb_shape_complete()
|
||||||
self.app.inform.emit('[success] %s' %
|
self.app.inform.emit('[success] %s' % _("Done."))
|
||||||
_("Done."))
|
|
||||||
|
|
||||||
# MS: always return to the Select Tool if modifier key is not pressed
|
# MS: always return to the Select Tool if modifier key is not pressed
|
||||||
# else return to the current tool but not for FCTrack
|
# else return to the current tool but not for FCTrack
|
||||||
@@ -4774,18 +4763,23 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||||||
|
|
||||||
self.app.mouse = [x, y]
|
self.app.mouse = [x, y]
|
||||||
|
|
||||||
# update the position label in the infobar since the APP mouse event handlers are disconnected
|
|
||||||
self.app.ui.position_label.setText(" <b>X</b>: %.4f "
|
|
||||||
"<b>Y</b>: %.4f" % (x, y))
|
|
||||||
|
|
||||||
if self.pos is None:
|
if self.pos is None:
|
||||||
self.pos = (0, 0)
|
self.pos = (0, 0)
|
||||||
self.app.dx = x - self.pos[0]
|
self.app.dx = x - self.pos[0]
|
||||||
self.app.dy = y - self.pos[1]
|
self.app.dy = y - self.pos[1]
|
||||||
|
|
||||||
# update the reference position label in the infobar since the APP mouse event handlers are disconnected
|
# # update the position label in the infobar since the APP mouse event handlers are disconnected
|
||||||
self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f <b>Dy</b>: "
|
self.app.ui.position_label.setText(" <b>X</b>: %.4f "
|
||||||
"%.4f " % (self.app.dx, self.app.dy))
|
"<b>Y</b>: %.4f " % (x, y))
|
||||||
|
#
|
||||||
|
# # update the reference position label in the infobar since the APP mouse event handlers are disconnected
|
||||||
|
# self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f <b>Dy</b>: "
|
||||||
|
# "%.4f " % (self.app.dx, self.app.dy))
|
||||||
|
|
||||||
|
units = self.app.defaults["units"].lower()
|
||||||
|
self.app.plotcanvas.text_hud.text = \
|
||||||
|
'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\n\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format(
|
||||||
|
self.app.dx, units, self.app.dy, units, x, units, y, units)
|
||||||
|
|
||||||
self.update_utility_geometry(data=(x, y))
|
self.update_utility_geometry(data=(x, y))
|
||||||
|
|
||||||
@@ -5032,7 +5026,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||||||
|
|
||||||
def select_tool(self, toolname):
|
def select_tool(self, toolname):
|
||||||
"""
|
"""
|
||||||
Selects a drawing tool. Impacts the object and GUI.
|
Selects a drawing tool. Impacts the object and AppGUI.
|
||||||
|
|
||||||
:param toolname: Name of the tool.
|
:param toolname: Name of the tool.
|
||||||
:return: None
|
:return: None
|
||||||
@@ -5298,7 +5292,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||||||
self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
|
self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
|
||||||
|
|
||||||
|
|
||||||
class TransformEditorTool(FlatCAMTool):
|
class TransformEditorTool(AppTool):
|
||||||
"""
|
"""
|
||||||
Inputs to specify how to paint the selected polygons.
|
Inputs to specify how to paint the selected polygons.
|
||||||
"""
|
"""
|
||||||
@@ -5311,7 +5305,7 @@ class TransformEditorTool(FlatCAMTool):
|
|||||||
offsetName = _("Offset")
|
offsetName = _("Offset")
|
||||||
|
|
||||||
def __init__(self, app, draw_app):
|
def __init__(self, app, draw_app):
|
||||||
FlatCAMTool.__init__(self, app)
|
AppTool.__init__(self, app)
|
||||||
|
|
||||||
self.app = app
|
self.app = app
|
||||||
self.draw_app = draw_app
|
self.draw_app = draw_app
|
||||||
@@ -5633,7 +5627,7 @@ class TransformEditorTool(FlatCAMTool):
|
|||||||
"the 'y' in (x, y) will be used when using Flip on Y.")
|
"the 'y' in (x, y) will be used when using Flip on Y.")
|
||||||
)
|
)
|
||||||
self.flip_ref_label.setMinimumWidth(50)
|
self.flip_ref_label.setMinimumWidth(50)
|
||||||
self.flip_ref_entry = EvalEntry2("(0, 0)")
|
self.flip_ref_entry = FCEntry()
|
||||||
self.flip_ref_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
|
self.flip_ref_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
|
||||||
# self.flip_ref_entry.setFixedWidth(60)
|
# self.flip_ref_entry.setFixedWidth(60)
|
||||||
|
|
||||||
@@ -5697,13 +5691,13 @@ class TransformEditorTool(FlatCAMTool):
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
FlatCAMTool.run(self)
|
AppTool.run(self)
|
||||||
self.set_tool_ui()
|
self.set_tool_ui()
|
||||||
|
|
||||||
self.app.ui.notebook.setTabText(2, _("Transform Tool"))
|
self.app.ui.notebook.setTabText(2, _("Transform Tool"))
|
||||||
|
|
||||||
def install(self, icon=None, separator=None, **kwargs):
|
def install(self, icon=None, separator=None, **kwargs):
|
||||||
FlatCAMTool.install(self, icon, separator, shortcut='Alt+T', **kwargs)
|
AppTool.install(self, icon, separator, shortcut='Alt+T', **kwargs)
|
||||||
|
|
||||||
def set_tool_ui(self):
|
def set_tool_ui(self):
|
||||||
# Initialize form
|
# Initialize form
|
||||||
@@ -5760,7 +5754,7 @@ class TransformEditorTool(FlatCAMTool):
|
|||||||
if self.app.defaults["tools_transform_mirror_point"]:
|
if self.app.defaults["tools_transform_mirror_point"]:
|
||||||
self.flip_ref_entry.set_value(self.app.defaults["tools_transform_mirror_point"])
|
self.flip_ref_entry.set_value(self.app.defaults["tools_transform_mirror_point"])
|
||||||
else:
|
else:
|
||||||
self.flip_ref_entry.set_value((0, 0))
|
self.flip_ref_entry.set_value("0, 0")
|
||||||
|
|
||||||
def template(self):
|
def template(self):
|
||||||
if not self.draw_app.selected:
|
if not self.draw_app.selected:
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
# MIT Licence #
|
# MIT Licence #
|
||||||
# ##########################################################
|
# ##########################################################
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCFileSaveDialog, FCEntry, FCTextAreaExtended, FCTextAreaLineNumber
|
from AppGUI.GUIElements import FCFileSaveDialog, FCEntry, FCTextAreaExtended, FCTextAreaLineNumber
|
||||||
from PyQt5 import QtPrintSupport, QtWidgets, QtCore, QtGui
|
from PyQt5 import QtPrintSupport, QtWidgets, QtCore, QtGui
|
||||||
|
|
||||||
from reportlab.platypus import SimpleDocTemplate, Paragraph
|
from reportlab.platypus import SimpleDocTemplate, Paragraph
|
||||||
@@ -15,7 +15,7 @@ from reportlab.lib.units import inch, mm
|
|||||||
# from io import StringIO
|
# from io import StringIO
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -214,10 +214,10 @@ class TextEditor(QtWidgets.QWidget):
|
|||||||
filename = str(FCFileSaveDialog.get_saved_filename(
|
filename = str(FCFileSaveDialog.get_saved_filename(
|
||||||
caption=_("Export Code ..."),
|
caption=_("Export Code ..."),
|
||||||
directory=self.app.defaults["global_last_folder"] + '/' + str(obj_name),
|
directory=self.app.defaults["global_last_folder"] + '/' + str(obj_name),
|
||||||
filter=_filter_
|
ext_filter=_filter_
|
||||||
)[0])
|
)[0])
|
||||||
except TypeError:
|
except TypeError:
|
||||||
filename = str(FCFileSaveDialog.get_saved_filename(caption=_("Export Code ..."), filter=_filter_)[0])
|
filename = str(FCFileSaveDialog.get_saved_filename(caption=_("Export Code ..."), ext_filter=_filter_)[0])
|
||||||
|
|
||||||
if filename == "":
|
if filename == "":
|
||||||
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled."))
|
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled."))
|
||||||
@@ -20,9 +20,10 @@ from copy import copy
|
|||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
import html
|
import html
|
||||||
|
import sys
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
log = logging.getLogger('base')
|
log = logging.getLogger('base')
|
||||||
@@ -569,9 +570,13 @@ class FCEntry3(FCEntry):
|
|||||||
|
|
||||||
|
|
||||||
class EvalEntry(QtWidgets.QLineEdit):
|
class EvalEntry(QtWidgets.QLineEdit):
|
||||||
def __init__(self, parent=None):
|
def __init__(self, border_color=None, parent=None):
|
||||||
super(EvalEntry, self).__init__(parent)
|
super(EvalEntry, self).__init__(parent)
|
||||||
self.readyToEdit = True
|
self.readyToEdit = True
|
||||||
|
|
||||||
|
if border_color:
|
||||||
|
self.setStyleSheet("QLineEdit {border: 1px solid %s;}" % border_color)
|
||||||
|
|
||||||
self.editingFinished.connect(self.on_edit_finished)
|
self.editingFinished.connect(self.on_edit_finished)
|
||||||
|
|
||||||
def on_edit_finished(self):
|
def on_edit_finished(self):
|
||||||
@@ -598,7 +603,6 @@ class EvalEntry(QtWidgets.QLineEdit):
|
|||||||
|
|
||||||
def get_value(self):
|
def get_value(self):
|
||||||
raw = str(self.text()).strip(' ')
|
raw = str(self.text()).strip(' ')
|
||||||
evaled = 0.0
|
|
||||||
try:
|
try:
|
||||||
evaled = eval(raw)
|
evaled = eval(raw)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -638,7 +642,7 @@ class EvalEntry2(QtWidgets.QLineEdit):
|
|||||||
|
|
||||||
def get_value(self):
|
def get_value(self):
|
||||||
raw = str(self.text()).strip(' ')
|
raw = str(self.text()).strip(' ')
|
||||||
evaled = 0.0
|
|
||||||
try:
|
try:
|
||||||
evaled = eval(raw)
|
evaled = eval(raw)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -655,6 +659,30 @@ class EvalEntry2(QtWidgets.QLineEdit):
|
|||||||
return QtCore.QSize(EDIT_SIZE_HINT, default_hint_size.height())
|
return QtCore.QSize(EDIT_SIZE_HINT, default_hint_size.height())
|
||||||
|
|
||||||
|
|
||||||
|
class NumericalEvalEntry(EvalEntry):
|
||||||
|
"""
|
||||||
|
Will evaluate the input and return a value. Accepts only float numbers and formulas using the operators: /,*,+,-,%
|
||||||
|
"""
|
||||||
|
def __init__(self, border_color=None):
|
||||||
|
super().__init__(border_color=border_color)
|
||||||
|
|
||||||
|
regex = QtCore.QRegExp("[0-9\/\*\+\-\%\.\s]*")
|
||||||
|
validator = QtGui.QRegExpValidator(regex, self)
|
||||||
|
self.setValidator(validator)
|
||||||
|
|
||||||
|
|
||||||
|
class NumericalEvalTupleEntry(FCEntry):
|
||||||
|
"""
|
||||||
|
Will evaluate the input and return a value. Accepts only float numbers and formulas using the operators: /,*,+,-,%
|
||||||
|
"""
|
||||||
|
def __init__(self, border_color=None):
|
||||||
|
super().__init__(border_color=border_color)
|
||||||
|
|
||||||
|
regex = QtCore.QRegExp("[0-9\/\*\+\-\%\.\s\,]*")
|
||||||
|
validator = QtGui.QRegExpValidator(regex, self)
|
||||||
|
self.setValidator(validator)
|
||||||
|
|
||||||
|
|
||||||
class FCSpinner(QtWidgets.QSpinBox):
|
class FCSpinner(QtWidgets.QSpinBox):
|
||||||
|
|
||||||
returnPressed = QtCore.pyqtSignal()
|
returnPressed = QtCore.pyqtSignal()
|
||||||
@@ -683,6 +711,8 @@ class FCSpinner(QtWidgets.QSpinBox):
|
|||||||
self.setAlignment(align_val)
|
self.setAlignment(align_val)
|
||||||
|
|
||||||
self.prev_readyToEdit = True
|
self.prev_readyToEdit = True
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Preferred)
|
||||||
|
self.setSizePolicy(sizePolicy)
|
||||||
|
|
||||||
def eventFilter(self, object, event):
|
def eventFilter(self, object, event):
|
||||||
if event.type() == QtCore.QEvent.MouseButtonPress and self.prev_readyToEdit is True:
|
if event.type() == QtCore.QEvent.MouseButtonPress and self.prev_readyToEdit is True:
|
||||||
@@ -815,6 +845,8 @@ class FCDoubleSpinner(QtWidgets.QDoubleSpinBox):
|
|||||||
self.setAlignment(align_val)
|
self.setAlignment(align_val)
|
||||||
|
|
||||||
self.prev_readyToEdit = True
|
self.prev_readyToEdit = True
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Preferred)
|
||||||
|
self.setSizePolicy(sizePolicy)
|
||||||
|
|
||||||
def on_edit_finished(self):
|
def on_edit_finished(self):
|
||||||
self.clearFocus()
|
self.clearFocus()
|
||||||
@@ -1336,7 +1368,8 @@ class FCComboBox(QtWidgets.QComboBox):
|
|||||||
return str(self.currentText())
|
return str(self.currentText())
|
||||||
|
|
||||||
def set_value(self, val):
|
def set_value(self, val):
|
||||||
self.setCurrentIndex(self.findText(str(val)))
|
idx = self.findText(str(val))
|
||||||
|
self.setCurrentIndex(idx)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_last(self):
|
def is_last(self):
|
||||||
@@ -1480,9 +1513,11 @@ class FCDetachableTab(QtWidgets.QTabWidget):
|
|||||||
From here:
|
From here:
|
||||||
https://stackoverflow.com/questions/47267195/in-pyqt4-is-it-possible-to-detach-tabs-from-a-qtabwidget
|
https://stackoverflow.com/questions/47267195/in-pyqt4-is-it-possible-to-detach-tabs-from-a-qtabwidget
|
||||||
"""
|
"""
|
||||||
|
tab_detached = QtCore.pyqtSignal(str)
|
||||||
|
tab_attached = QtCore.pyqtSignal(str)
|
||||||
|
|
||||||
def __init__(self, protect=None, protect_by_name=None, parent=None):
|
def __init__(self, protect=None, protect_by_name=None, parent=None):
|
||||||
super().__init__()
|
super().__init__(parent=parent)
|
||||||
|
|
||||||
self.tabBar = self.FCTabBar(self)
|
self.tabBar = self.FCTabBar(self)
|
||||||
self.tabBar.onMoveTabSignal.connect(self.moveTab)
|
self.tabBar.onMoveTabSignal.connect(self.moveTab)
|
||||||
@@ -1619,7 +1654,7 @@ class FCDetachableTab(QtWidgets.QTabWidget):
|
|||||||
self.insertTab(toIndex, widget, icon, text)
|
self.insertTab(toIndex, widget, icon, text)
|
||||||
self.setCurrentIndex(toIndex)
|
self.setCurrentIndex(toIndex)
|
||||||
|
|
||||||
@pyqtSlot(int, QtCore.QPoint)
|
# @pyqtSlot(int, QtCore.QPoint)
|
||||||
def detachTab(self, index, point):
|
def detachTab(self, index, point):
|
||||||
"""
|
"""
|
||||||
Detach the tab by removing it's contents and placing them in
|
Detach the tab by removing it's contents and placing them in
|
||||||
@@ -1656,6 +1691,8 @@ class FCDetachableTab(QtWidgets.QTabWidget):
|
|||||||
# Create a reference to maintain access to the detached tab
|
# Create a reference to maintain access to the detached tab
|
||||||
self.detachedTabs[name] = detachedTab
|
self.detachedTabs[name] = detachedTab
|
||||||
|
|
||||||
|
self.tab_detached.emit(name)
|
||||||
|
|
||||||
def attachTab(self, contentWidget, name, icon, insertAt=None):
|
def attachTab(self, contentWidget, name, icon, insertAt=None):
|
||||||
"""
|
"""
|
||||||
Re-attach the tab by removing the content from the DetachedTab window,
|
Re-attach the tab by removing the content from the DetachedTab window,
|
||||||
@@ -1668,11 +1705,11 @@ class FCDetachableTab(QtWidgets.QTabWidget):
|
|||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
old_name = name
|
||||||
|
|
||||||
# Make the content widget a child of this widget
|
# Make the content widget a child of this widget
|
||||||
contentWidget.setParent(self)
|
contentWidget.setParent(self)
|
||||||
|
|
||||||
# Remove the reference
|
|
||||||
del self.detachedTabs[name]
|
|
||||||
# make sure that we strip the 'FlatCAM' part of the detached name otherwise the tab name will be too long
|
# make sure that we strip the 'FlatCAM' part of the detached name otherwise the tab name will be too long
|
||||||
name = name.partition(' ')[2]
|
name = name.partition(' ')[2]
|
||||||
|
|
||||||
@@ -1712,6 +1749,9 @@ class FCDetachableTab(QtWidgets.QTabWidget):
|
|||||||
else:
|
else:
|
||||||
index = self.insertTab(insert_index, contentWidget, icon, name)
|
index = self.insertTab(insert_index, contentWidget, icon, name)
|
||||||
|
|
||||||
|
obj_name = contentWidget.objectName()
|
||||||
|
self.tab_attached.emit(obj_name)
|
||||||
|
|
||||||
# on reattaching the tab if protect is true then the closure button is not added
|
# on reattaching the tab if protect is true then the closure button is not added
|
||||||
if self.protect_tab is True:
|
if self.protect_tab is True:
|
||||||
self.protectTab(index)
|
self.protectTab(index)
|
||||||
@@ -1727,6 +1767,14 @@ class FCDetachableTab(QtWidgets.QTabWidget):
|
|||||||
if index > -1:
|
if index > -1:
|
||||||
self.setCurrentIndex(insert_index) if self.use_old_index else self.setCurrentIndex(index)
|
self.setCurrentIndex(insert_index) if self.use_old_index else self.setCurrentIndex(index)
|
||||||
|
|
||||||
|
# Remove the reference
|
||||||
|
# Unix-like OS's crash with segmentation fault after this. FOr whatever reason, they loose reference
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
try:
|
||||||
|
del self.detachedTabs[old_name]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
def removeTabByName(self, name):
|
def removeTabByName(self, name):
|
||||||
"""
|
"""
|
||||||
Remove the tab with the given name, even if it is detached
|
Remove the tab with the given name, even if it is detached
|
||||||
@@ -2180,10 +2228,13 @@ class OptionalHideInputSection:
|
|||||||
Associates the a checkbox with a set of inputs.
|
Associates the a checkbox with a set of inputs.
|
||||||
|
|
||||||
:param cb: Checkbox that enables the optional inputs.
|
:param cb: Checkbox that enables the optional inputs.
|
||||||
|
:type cb: QtWidgets.QCheckBox
|
||||||
:param optinputs: List of widgets that are optional.
|
:param optinputs: List of widgets that are optional.
|
||||||
|
:type optinputs: list
|
||||||
:param logic: When True the logic is normal, when False the logic is in reverse
|
:param logic: When True the logic is normal, when False the logic is in reverse
|
||||||
It means that for logic=True, when the checkbox is checked the widgets are Enabled, and
|
It means that for logic=True, when the checkbox is checked the widgets are Enabled, and
|
||||||
for logic=False, when the checkbox is checked the widgets are Disabled
|
for logic=False, when the checkbox is checked the widgets are Disabled
|
||||||
|
:type logic: bool
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
assert isinstance(cb, FCCheckBox), \
|
assert isinstance(cb, FCCheckBox), \
|
||||||
@@ -2215,6 +2266,7 @@ class OptionalHideInputSection:
|
|||||||
class FCTable(QtWidgets.QTableWidget):
|
class FCTable(QtWidgets.QTableWidget):
|
||||||
|
|
||||||
drag_drop_sig = QtCore.pyqtSignal()
|
drag_drop_sig = QtCore.pyqtSignal()
|
||||||
|
lost_focus = QtCore.pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, drag_drop=False, protected_rows=None, parent=None):
|
def __init__(self, drag_drop=False, protected_rows=None, parent=None):
|
||||||
super(FCTable, self).__init__(parent)
|
super(FCTable, self).__init__(parent)
|
||||||
@@ -2278,6 +2330,10 @@ class FCTable(QtWidgets.QTableWidget):
|
|||||||
else:
|
else:
|
||||||
QtWidgets.QTableWidget.mousePressEvent(self, event)
|
QtWidgets.QTableWidget.mousePressEvent(self, event)
|
||||||
|
|
||||||
|
def focusOutEvent(self, event):
|
||||||
|
self.lost_focus.emit()
|
||||||
|
super().focusOutEvent(event)
|
||||||
|
|
||||||
def setupContextMenu(self):
|
def setupContextMenu(self):
|
||||||
self.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
|
self.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
|
||||||
|
|
||||||
@@ -2449,7 +2505,8 @@ class SpinBoxDelegate(QtWidgets.QItemDelegate):
|
|||||||
def updateEditorGeometry(self, editor, option, index):
|
def updateEditorGeometry(self, editor, option, index):
|
||||||
editor.setGeometry(option.rect)
|
editor.setGeometry(option.rect)
|
||||||
|
|
||||||
def setDecimals(self, spinbox, digits):
|
@staticmethod
|
||||||
|
def setDecimals(spinbox, digits):
|
||||||
spinbox.setDecimals(digits)
|
spinbox.setDecimals(digits)
|
||||||
|
|
||||||
|
|
||||||
@@ -2497,7 +2554,7 @@ class DialogBoxRadio(QtWidgets.QDialog):
|
|||||||
:param title: string with the window title
|
:param title: string with the window title
|
||||||
:param label: string with the message inside the dialog box
|
:param label: string with the message inside the dialog box
|
||||||
"""
|
"""
|
||||||
super(DialogBoxRadio, self).__init__()
|
super(DialogBoxRadio, self).__init__(parent=parent)
|
||||||
if initial_text is None:
|
if initial_text is None:
|
||||||
self.location = str((0, 0))
|
self.location = str((0, 0))
|
||||||
else:
|
else:
|
||||||
@@ -2740,9 +2797,12 @@ class MyCompleter(QCompleter):
|
|||||||
insertText = QtCore.pyqtSignal(str)
|
insertText = QtCore.pyqtSignal(str)
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QCompleter.__init__(self)
|
QCompleter.__init__(self, parent=parent)
|
||||||
self.setCompletionMode(QCompleter.PopupCompletion)
|
self.setCompletionMode(QCompleter.PopupCompletion)
|
||||||
self.highlighted.connect(self.setHighlighted)
|
self.highlighted.connect(self.setHighlighted)
|
||||||
|
|
||||||
|
self.lastSelected = ''
|
||||||
|
|
||||||
# self.popup().installEventFilter(self)
|
# self.popup().installEventFilter(self)
|
||||||
|
|
||||||
# def eventFilter(self, obj, event):
|
# def eventFilter(self, obj, event):
|
||||||
@@ -2898,9 +2958,9 @@ class FCFileSaveDialog(QtWidgets.QFileDialog):
|
|||||||
super(FCFileSaveDialog, self).__init__(*args)
|
super(FCFileSaveDialog, self).__init__(*args)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_saved_filename(parent=None, caption='', directory='', filter='', initialFilter=''):
|
def get_saved_filename(parent=None, caption='', directory='', ext_filter='', initialFilter=''):
|
||||||
filename, _filter = QtWidgets.QFileDialog.getSaveFileName(parent=parent, caption=caption,
|
filename, _filter = QtWidgets.QFileDialog.getSaveFileName(parent=parent, caption=caption,
|
||||||
directory=directory, filter=filter,
|
directory=directory, filter=ext_filter,
|
||||||
initialFilter=initialFilter)
|
initialFilter=initialFilter)
|
||||||
|
|
||||||
filename = str(filename)
|
filename = str(filename)
|
||||||
@@ -2916,6 +2976,225 @@ class FCFileSaveDialog(QtWidgets.QFileDialog):
|
|||||||
return filename, _filter
|
return filename, _filter
|
||||||
|
|
||||||
|
|
||||||
|
class FCDock(QtWidgets.QDockWidget):
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(FCDock, self).__init__(*args)
|
||||||
|
self.close_callback = kwargs["close_callback"] if "close_callback" in kwargs else None
|
||||||
|
|
||||||
|
def closeEvent(self, event: QtGui.QCloseEvent) -> None:
|
||||||
|
self.close_callback()
|
||||||
|
super().closeEvent(event)
|
||||||
|
|
||||||
|
def show(self) -> None:
|
||||||
|
if self.isFloating():
|
||||||
|
self.setFloating(False)
|
||||||
|
super().show()
|
||||||
|
|
||||||
|
|
||||||
|
class FlatCAMActivityView(QtWidgets.QWidget):
|
||||||
|
"""
|
||||||
|
This class create and control the activity icon displayed in the App status bar
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, app, parent=None):
|
||||||
|
super().__init__(parent=parent)
|
||||||
|
|
||||||
|
self.app = app
|
||||||
|
|
||||||
|
if self.app.defaults["global_activity_icon"] == "Ball green":
|
||||||
|
icon = self.app.resource_location + '/active_2_static.png'
|
||||||
|
movie = self.app.resource_location + "/active_2.gif"
|
||||||
|
elif self.app.defaults["global_activity_icon"] == "Ball black":
|
||||||
|
icon = self.app.resource_location + '/active_static.png'
|
||||||
|
movie = self.app.resource_location + "/active.gif"
|
||||||
|
elif self.app.defaults["global_activity_icon"] == "Arrow green":
|
||||||
|
icon = self.app.resource_location + '/active_3_static.png'
|
||||||
|
movie = self.app.resource_location + "/active_3.gif"
|
||||||
|
elif self.app.defaults["global_activity_icon"] == "Eclipse green":
|
||||||
|
icon = self.app.resource_location + '/active_4_static.png'
|
||||||
|
movie = self.app.resource_location + "/active_4.gif"
|
||||||
|
else:
|
||||||
|
icon = self.app.resource_location + '/active_static.png'
|
||||||
|
movie = self.app.resource_location + "/active.gif"
|
||||||
|
|
||||||
|
self.setMinimumWidth(200)
|
||||||
|
self.movie_path = movie
|
||||||
|
self.icon_path = icon
|
||||||
|
|
||||||
|
self.icon = FCLabel(self)
|
||||||
|
self.icon.setGeometry(0, 0, 16, 12)
|
||||||
|
self.movie = QtGui.QMovie(self.movie_path)
|
||||||
|
|
||||||
|
self.icon.setMovie(self.movie)
|
||||||
|
# self.movie.start()
|
||||||
|
|
||||||
|
layout = QtWidgets.QHBoxLayout()
|
||||||
|
layout.setContentsMargins(5, 0, 5, 0)
|
||||||
|
layout.setAlignment(QtCore.Qt.AlignLeft)
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
layout.addWidget(self.icon)
|
||||||
|
self.text = QtWidgets.QLabel(self)
|
||||||
|
self.text.setText(_("Idle."))
|
||||||
|
self.icon.setPixmap(QtGui.QPixmap(self.icon_path))
|
||||||
|
|
||||||
|
layout.addWidget(self.text)
|
||||||
|
|
||||||
|
self.icon.clicked.connect(self.app.on_toolbar_replot)
|
||||||
|
|
||||||
|
def set_idle(self):
|
||||||
|
self.movie.stop()
|
||||||
|
self.text.setText(_("Idle."))
|
||||||
|
|
||||||
|
def set_busy(self, msg, no_movie=None):
|
||||||
|
if no_movie is not True:
|
||||||
|
self.icon.setMovie(self.movie)
|
||||||
|
self.movie.start()
|
||||||
|
self.text.setText(msg)
|
||||||
|
|
||||||
|
|
||||||
|
class FlatCAMInfoBar(QtWidgets.QWidget):
|
||||||
|
"""
|
||||||
|
This class create a place to display the App messages in the Status Bar
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, parent=None, app=None):
|
||||||
|
super(FlatCAMInfoBar, self).__init__(parent=parent)
|
||||||
|
|
||||||
|
self.app = app
|
||||||
|
|
||||||
|
self.icon = QtWidgets.QLabel(self)
|
||||||
|
self.icon.setGeometry(0, 0, 12, 12)
|
||||||
|
self.pmap = QtGui.QPixmap(self.app.resource_location + '/graylight12.png')
|
||||||
|
self.icon.setPixmap(self.pmap)
|
||||||
|
|
||||||
|
self.lock_pmaps = False
|
||||||
|
|
||||||
|
layout = QtWidgets.QHBoxLayout()
|
||||||
|
layout.setContentsMargins(5, 0, 5, 0)
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
layout.addWidget(self.icon)
|
||||||
|
|
||||||
|
self.text = QtWidgets.QLabel(self)
|
||||||
|
self.text.setText(_("Application started ..."))
|
||||||
|
self.text.setToolTip(_("Hello!"))
|
||||||
|
|
||||||
|
layout.addWidget(self.text)
|
||||||
|
layout.addStretch()
|
||||||
|
|
||||||
|
def set_text_(self, text, color=None):
|
||||||
|
self.text.setText(text)
|
||||||
|
self.text.setToolTip(text)
|
||||||
|
if color:
|
||||||
|
self.text.setStyleSheet('color: %s' % str(color))
|
||||||
|
|
||||||
|
def set_status(self, text, level="info"):
|
||||||
|
level = str(level)
|
||||||
|
|
||||||
|
if self.lock_pmaps is not True:
|
||||||
|
self.pmap.fill()
|
||||||
|
if level == "ERROR" or level == "ERROR_NOTCL":
|
||||||
|
self.pmap = QtGui.QPixmap(self.app.resource_location + '/redlight12.png')
|
||||||
|
elif level.lower() == "success":
|
||||||
|
self.pmap = QtGui.QPixmap(self.app.resource_location + '/greenlight12.png')
|
||||||
|
elif level == "WARNING" or level == "WARNING_NOTCL":
|
||||||
|
self.pmap = QtGui.QPixmap(self.app.resource_location + '/yellowlight12.png')
|
||||||
|
elif level.lower() == "selected":
|
||||||
|
self.pmap = QtGui.QPixmap(self.app.resource_location + '/bluelight12.png')
|
||||||
|
else:
|
||||||
|
self.pmap = QtGui.QPixmap(self.app.resource_location + '/graylight12.png')
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.set_text_(text)
|
||||||
|
self.icon.setPixmap(self.pmap)
|
||||||
|
except Exception as e:
|
||||||
|
log.debug("FlatCAMInfoBar.set_status() --> %s" % str(e))
|
||||||
|
|
||||||
|
|
||||||
|
class FlatCAMSystemTray(QtWidgets.QSystemTrayIcon):
|
||||||
|
"""
|
||||||
|
This class create the Sys Tray icon for the app
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, app, icon, headless=None, parent=None):
|
||||||
|
# QtWidgets.QSystemTrayIcon.__init__(self, icon, parent)
|
||||||
|
super().__init__(icon, parent=parent)
|
||||||
|
self.app = app
|
||||||
|
|
||||||
|
menu = QtWidgets.QMenu(parent)
|
||||||
|
|
||||||
|
menu_runscript = QtWidgets.QAction(QtGui.QIcon(self.app.resource_location + '/script14.png'),
|
||||||
|
'%s' % _('Run Script ...'), self)
|
||||||
|
menu_runscript.setToolTip(
|
||||||
|
_("Will run the opened Tcl Script thus\n"
|
||||||
|
"enabling the automation of certain\n"
|
||||||
|
"functions of FlatCAM.")
|
||||||
|
)
|
||||||
|
menu.addAction(menu_runscript)
|
||||||
|
|
||||||
|
menu.addSeparator()
|
||||||
|
|
||||||
|
if headless is None:
|
||||||
|
self.menu_open = menu.addMenu(QtGui.QIcon(self.app.resource_location + '/folder32_bis.png'), _('Open'))
|
||||||
|
|
||||||
|
# Open Project ...
|
||||||
|
menu_openproject = QtWidgets.QAction(QtGui.QIcon(self.app.resource_location + '/folder16.png'),
|
||||||
|
_('Open Project ...'), self)
|
||||||
|
self.menu_open.addAction(menu_openproject)
|
||||||
|
self.menu_open.addSeparator()
|
||||||
|
|
||||||
|
# Open Gerber ...
|
||||||
|
menu_opengerber = QtWidgets.QAction(QtGui.QIcon(self.app.resource_location + '/flatcam_icon24.png'),
|
||||||
|
_('Open &Gerber ...\tCtrl+G'), self)
|
||||||
|
self.menu_open.addAction(menu_opengerber)
|
||||||
|
|
||||||
|
# Open Excellon ...
|
||||||
|
menu_openexcellon = QtWidgets.QAction(QtGui.QIcon(self.app.resource_location + '/open_excellon32.png'),
|
||||||
|
_('Open &Excellon ...\tCtrl+E'), self)
|
||||||
|
self.menu_open.addAction(menu_openexcellon)
|
||||||
|
|
||||||
|
# Open G-Code ...
|
||||||
|
menu_opengcode = QtWidgets.QAction(QtGui.QIcon(self.app.resource_location + '/code.png'),
|
||||||
|
_('Open G-&Code ...'), self)
|
||||||
|
self.menu_open.addAction(menu_opengcode)
|
||||||
|
|
||||||
|
self.menu_open.addSeparator()
|
||||||
|
|
||||||
|
menu_openproject.triggered.connect(self.app.on_file_openproject)
|
||||||
|
menu_opengerber.triggered.connect(self.app.on_fileopengerber)
|
||||||
|
menu_openexcellon.triggered.connect(self.app.on_fileopenexcellon)
|
||||||
|
menu_opengcode.triggered.connect(self.app.on_fileopengcode)
|
||||||
|
|
||||||
|
exitAction = menu.addAction(_("Exit"))
|
||||||
|
exitAction.setIcon(QtGui.QIcon(self.app.resource_location + '/power16.png'))
|
||||||
|
self.setContextMenu(menu)
|
||||||
|
|
||||||
|
menu_runscript.triggered.connect(lambda: self.app.on_filerunscript(
|
||||||
|
silent=True if self.app.cmd_line_headless == 1 else False))
|
||||||
|
|
||||||
|
exitAction.triggered.connect(self.app.final_save)
|
||||||
|
|
||||||
|
|
||||||
|
def message_dialog(title, message, kind="info", parent=None):
|
||||||
|
"""
|
||||||
|
Builds and show a custom QMessageBox to be used in FlatCAM.
|
||||||
|
|
||||||
|
:param title: title of the QMessageBox
|
||||||
|
:param message: message to be displayed
|
||||||
|
:param kind: type of QMessageBox; will display a specific icon.
|
||||||
|
:param parent: parent
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
icon = {"info": QtWidgets.QMessageBox.Information,
|
||||||
|
"warning": QtWidgets.QMessageBox.Warning,
|
||||||
|
"error": QtWidgets.QMessageBox.Critical}[str(kind)]
|
||||||
|
dlg = QtWidgets.QMessageBox(icon, title, message, parent=parent)
|
||||||
|
dlg.setText(message)
|
||||||
|
dlg.exec_()
|
||||||
|
|
||||||
|
|
||||||
def rreplace(s, old, new, occurrence):
|
def rreplace(s, old, new, occurrence):
|
||||||
"""
|
"""
|
||||||
Credits go here:
|
Credits go here:
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -8,13 +8,21 @@
|
|||||||
from PyQt5 import QtCore
|
from PyQt5 import QtCore
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from flatcamGUI.VisPyCanvas import VisPyCanvas, Color
|
from AppGUI.VisPyCanvas import VisPyCanvas, Color
|
||||||
from flatcamGUI.VisPyVisuals import ShapeGroup, ShapeCollection, TextCollection, TextGroup, Cursor
|
from AppGUI.VisPyVisuals import ShapeGroup, ShapeCollection, TextCollection, TextGroup, Cursor
|
||||||
from vispy.scene.visuals import InfiniteLine, Line
|
from vispy.scene.visuals import InfiniteLine, Line, Rectangle, Text
|
||||||
|
|
||||||
|
import gettext
|
||||||
|
import AppTranslation as fcTranslate
|
||||||
|
import builtins
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from vispy.geometry import Rect
|
from vispy.geometry import Rect
|
||||||
|
|
||||||
|
fcTranslate.apply_language('strings')
|
||||||
|
if '_' not in builtins.__dict__:
|
||||||
|
_ = gettext.gettext
|
||||||
|
|
||||||
log = logging.getLogger('base')
|
log = logging.getLogger('base')
|
||||||
|
|
||||||
|
|
||||||
@@ -54,8 +62,12 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
|
|||||||
|
|
||||||
if theme == 'white':
|
if theme == 'white':
|
||||||
self.line_color = (0.3, 0.0, 0.0, 1.0)
|
self.line_color = (0.3, 0.0, 0.0, 1.0)
|
||||||
|
self.rect_hud_color = Color('#0000FF10')
|
||||||
|
self.text_hud_color = 'black'
|
||||||
else:
|
else:
|
||||||
self.line_color = (0.4, 0.4, 0.4, 1.0)
|
self.line_color = (0.4, 0.4, 0.4, 1.0)
|
||||||
|
self.rect_hud_color = Color('#80808040')
|
||||||
|
self.text_hud_color = 'white'
|
||||||
|
|
||||||
# workspace lines; I didn't use the rectangle because I didn't want to add another VisPy Node,
|
# workspace lines; I didn't use the rectangle because I didn't want to add another VisPy Node,
|
||||||
# which might decrease performance
|
# which might decrease performance
|
||||||
@@ -129,11 +141,6 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
|
|||||||
self.h_line = InfiniteLine(pos=0, color=(0.70, 0.3, 0.3, 0.8), vertical=False,
|
self.h_line = InfiniteLine(pos=0, color=(0.70, 0.3, 0.3, 0.8), vertical=False,
|
||||||
parent=self.view.scene)
|
parent=self.view.scene)
|
||||||
|
|
||||||
# draw a rectangle made out of 4 lines on the canvas to serve as a hint for the work area
|
|
||||||
# all CNC have a limited workspace
|
|
||||||
if self.fcapp.defaults['global_workspace'] is True:
|
|
||||||
self.draw_workspace(workspace_size=self.fcapp.defaults["global_workspaceT"])
|
|
||||||
|
|
||||||
self.line_parent = None
|
self.line_parent = None
|
||||||
if self.fcapp.defaults["global_cursor_color_enabled"]:
|
if self.fcapp.defaults["global_cursor_color_enabled"]:
|
||||||
c_color = Color(self.fcapp.defaults["global_cursor_color"]).rgba
|
c_color = Color(self.fcapp.defaults["global_cursor_color"]).rgba
|
||||||
@@ -146,13 +153,61 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
|
|||||||
self.cursor_h_line = InfiniteLine(pos=None, color=c_color, vertical=False,
|
self.cursor_h_line = InfiniteLine(pos=None, color=c_color, vertical=False,
|
||||||
parent=self.line_parent)
|
parent=self.line_parent)
|
||||||
|
|
||||||
|
# font size
|
||||||
|
qsettings = QtCore.QSettings("Open Source", "FlatCAM")
|
||||||
|
if qsettings.contains("hud_font_size"):
|
||||||
|
fsize = qsettings.value('hud_font_size', type=int)
|
||||||
|
else:
|
||||||
|
fsize = 8
|
||||||
|
|
||||||
|
# units
|
||||||
|
units = self.fcapp.defaults["units"].lower()
|
||||||
|
|
||||||
|
# coordinates and anchors
|
||||||
|
height = fsize * 11 # 90. Constant 11 is something that works
|
||||||
|
width = height * 2 # width is double the height = it is something that works
|
||||||
|
center_x = (width / 2) + 5
|
||||||
|
center_y = (height / 2) + 5
|
||||||
|
|
||||||
|
# text
|
||||||
|
self.text_hud = Text('', color=self.text_hud_color, pos=(10, center_y), method='gpu', anchor_x='left',
|
||||||
|
parent=None)
|
||||||
|
self.text_hud.font_size = fsize
|
||||||
|
self.text_hud.text = 'Dx:\t%s [%s]\nDy:\t%s [%s]\n\nX: \t%s [%s]\nY: \t%s [%s]' % \
|
||||||
|
('0.0000', units, '0.0000', units, '0.0000', units, '0.0000', units)
|
||||||
|
|
||||||
|
# rectangle
|
||||||
|
self.rect_hud = Rectangle(center=(center_x, center_y), width=width, height=height, radius=[5, 5, 5, 5],
|
||||||
|
border_color=self.rect_hud_color, color=self.rect_hud_color, parent=None)
|
||||||
|
self.rect_hud.set_gl_state(depth_test=False)
|
||||||
|
|
||||||
|
# draw a rectangle made out of 4 lines on the canvas to serve as a hint for the work area
|
||||||
|
# all CNC have a limited workspace
|
||||||
|
if self.fcapp.defaults['global_workspace'] is True:
|
||||||
|
self.draw_workspace(workspace_size=self.fcapp.defaults["global_workspaceT"])
|
||||||
|
|
||||||
|
# HUD Display
|
||||||
|
self.hud_enabled = False
|
||||||
|
|
||||||
|
# enable the HUD if it is activated in FlatCAM Preferences
|
||||||
|
if self.fcapp.defaults['global_hud'] is True:
|
||||||
|
self.on_toggle_hud(state=True)
|
||||||
|
|
||||||
|
# Axis Display
|
||||||
|
self.axis_enabled = True
|
||||||
|
|
||||||
|
# enable Axis
|
||||||
|
self.on_toggle_axis(state=True)
|
||||||
|
|
||||||
|
# enable Grid lines
|
||||||
|
self.grid_lines_enabled = True
|
||||||
|
|
||||||
self.shape_collections = []
|
self.shape_collections = []
|
||||||
|
|
||||||
self.shape_collection = self.new_shape_collection()
|
self.shape_collection = self.new_shape_collection()
|
||||||
self.fcapp.pool_recreated.connect(self.on_pool_recreated)
|
self.fcapp.pool_recreated.connect(self.on_pool_recreated)
|
||||||
self.text_collection = self.new_text_collection()
|
self.text_collection = self.new_text_collection()
|
||||||
|
|
||||||
# TODO: Should be setting to show/hide CNC job annotations (global or per object)
|
|
||||||
self.text_collection.enabled = True
|
self.text_collection.enabled = True
|
||||||
|
|
||||||
self.c = None
|
self.c = None
|
||||||
@@ -163,6 +218,76 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
|
|||||||
|
|
||||||
self.graph_event_connect('mouse_wheel', self.on_mouse_scroll)
|
self.graph_event_connect('mouse_wheel', self.on_mouse_scroll)
|
||||||
|
|
||||||
|
def on_toggle_axis(self, signal=None, state=None):
|
||||||
|
if state is None:
|
||||||
|
state = not self.axis_enabled
|
||||||
|
|
||||||
|
if state:
|
||||||
|
self.axis_enabled = True
|
||||||
|
self.v_line.parent = self.view.scene
|
||||||
|
self.h_line.parent = self.view.scene
|
||||||
|
self.fcapp.ui.axis_status_label.setStyleSheet("""
|
||||||
|
QLabel
|
||||||
|
{
|
||||||
|
color: black;
|
||||||
|
background-color: peachpuff;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
self.fcapp.inform[str, bool].emit(_("Axis enabled."), False)
|
||||||
|
else:
|
||||||
|
self.axis_enabled = False
|
||||||
|
self.v_line.parent = None
|
||||||
|
self.h_line.parent = None
|
||||||
|
self.fcapp.ui.axis_status_label.setStyleSheet("")
|
||||||
|
self.fcapp.inform[str, bool].emit(_("Axis disabled."), False)
|
||||||
|
|
||||||
|
def on_toggle_hud(self, signal=None, state=None):
|
||||||
|
if state is None:
|
||||||
|
state = not self.hud_enabled
|
||||||
|
|
||||||
|
if state:
|
||||||
|
self.hud_enabled = True
|
||||||
|
self.rect_hud.parent = self.view
|
||||||
|
self.text_hud.parent = self.view
|
||||||
|
self.fcapp.defaults['global_hud'] = True
|
||||||
|
self.fcapp.ui.hud_label.setStyleSheet("""
|
||||||
|
QLabel
|
||||||
|
{
|
||||||
|
color: black;
|
||||||
|
background-color: lightblue;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
self.fcapp.inform[str, bool].emit(_("HUD enabled."), False)
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.hud_enabled = False
|
||||||
|
self.rect_hud.parent = None
|
||||||
|
self.text_hud.parent = None
|
||||||
|
self.fcapp.defaults['global_hud'] = False
|
||||||
|
self.fcapp.ui.hud_label.setStyleSheet("")
|
||||||
|
self.fcapp.inform[str, bool].emit(_("HUD disabled."), False)
|
||||||
|
|
||||||
|
def on_toggle_grid_lines(self):
|
||||||
|
state = not self.grid_lines_enabled
|
||||||
|
|
||||||
|
if state:
|
||||||
|
self.grid_lines_enabled = True
|
||||||
|
self.grid.parent = self.view.scene
|
||||||
|
self.fcapp.inform[str, bool].emit(_("Grid enabled."), False)
|
||||||
|
else:
|
||||||
|
self.grid_lines_enabled = False
|
||||||
|
self.grid.parent = None
|
||||||
|
self.fcapp.inform[str, bool].emit(_("Grid disabled."), False)
|
||||||
|
|
||||||
|
# HACK: enabling/disabling the cursor seams to somehow update the shapes on screen
|
||||||
|
# - perhaps is a bug in VisPy implementation
|
||||||
|
if self.fcapp.grid_status():
|
||||||
|
self.fcapp.app_cursor.enabled = False
|
||||||
|
self.fcapp.app_cursor.enabled = True
|
||||||
|
else:
|
||||||
|
self.fcapp.app_cursor.enabled = True
|
||||||
|
self.fcapp.app_cursor.enabled = False
|
||||||
|
|
||||||
def draw_workspace(self, workspace_size):
|
def draw_workspace(self, workspace_size):
|
||||||
"""
|
"""
|
||||||
Draw a rectangular shape on canvas to specify our valid workspace.
|
Draw a rectangular shape on canvas to specify our valid workspace.
|
||||||
@@ -183,17 +308,30 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
|
|||||||
|
|
||||||
a = np.array([(0, 0), (dims[0], 0), (dims[0], dims[1]), (0, dims[1])])
|
a = np.array([(0, 0), (dims[0], 0), (dims[0], dims[1]), (0, dims[1])])
|
||||||
|
|
||||||
if not self.workspace_line:
|
# if not self.workspace_line:
|
||||||
|
# self.workspace_line = Line(pos=np.array((a[0], a[1], a[2], a[3], a[0])), color=(0.70, 0.3, 0.3, 0.7),
|
||||||
|
# antialias=True, method='agg', parent=self.view.scene)
|
||||||
|
# else:
|
||||||
|
# self.workspace_line.parent = self.view.scene
|
||||||
self.workspace_line = Line(pos=np.array((a[0], a[1], a[2], a[3], a[0])), color=(0.70, 0.3, 0.3, 0.7),
|
self.workspace_line = Line(pos=np.array((a[0], a[1], a[2], a[3], a[0])), color=(0.70, 0.3, 0.3, 0.7),
|
||||||
antialias=True, method='agg', parent=self.view.scene)
|
antialias=True, method='agg', parent=self.view.scene)
|
||||||
else:
|
|
||||||
self.workspace_line.parent = self.view.scene
|
self.fcapp.ui.wplace_label.set_value(workspace_size[:3])
|
||||||
|
self.fcapp.ui.wplace_label.setToolTip(workspace_size)
|
||||||
|
self.fcapp.ui.wplace_label.setStyleSheet("""
|
||||||
|
QLabel
|
||||||
|
{
|
||||||
|
color: black;
|
||||||
|
background-color: lightgreen;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
def delete_workspace(self):
|
def delete_workspace(self):
|
||||||
try:
|
try:
|
||||||
self.workspace_line.parent = None
|
self.workspace_line.parent = None
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
self.fcapp.ui.wplace_label.setStyleSheet("")
|
||||||
|
|
||||||
# redraw the workspace lines on the plot by re adding them to the parent view.scene
|
# redraw the workspace lines on the plot by re adding them to the parent view.scene
|
||||||
def restore_workspace(self):
|
def restore_workspace(self):
|
||||||
@@ -287,6 +425,7 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
|
|||||||
def on_mouse_scroll(self, event):
|
def on_mouse_scroll(self, event):
|
||||||
# key modifiers
|
# key modifiers
|
||||||
modifiers = event.modifiers
|
modifiers = event.modifiers
|
||||||
|
|
||||||
pan_delta_x = self.fcapp.defaults["global_gridx"]
|
pan_delta_x = self.fcapp.defaults["global_gridx"]
|
||||||
pan_delta_y = self.fcapp.defaults["global_gridy"]
|
pan_delta_y = self.fcapp.defaults["global_gridy"]
|
||||||
curr_pos = event.pos
|
curr_pos = event.pos
|
||||||
@@ -19,8 +19,10 @@ from shapely.geometry import Polygon, LineString, LinearRing
|
|||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
# Prevent conflict with Qt5 and above.
|
# Prevent conflict with Qt5 and above.
|
||||||
@@ -29,13 +31,13 @@ mpl_use("Qt5Agg")
|
|||||||
from matplotlib.figure import Figure
|
from matplotlib.figure import Figure
|
||||||
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
||||||
from matplotlib.lines import Line2D
|
from matplotlib.lines import Line2D
|
||||||
|
from matplotlib.offsetbox import AnchoredText
|
||||||
# from matplotlib.widgets import Cursor
|
# from matplotlib.widgets import Cursor
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
if '_' not in builtins.__dict__:
|
if '_' not in builtins.__dict__:
|
||||||
_ = gettext.gettext
|
_ = gettext.gettext
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger('base')
|
log = logging.getLogger('base')
|
||||||
|
|
||||||
|
|
||||||
@@ -45,9 +47,9 @@ class CanvasCache(QtCore.QObject):
|
|||||||
Case story #1:
|
Case story #1:
|
||||||
|
|
||||||
1) No objects in the project.
|
1) No objects in the project.
|
||||||
2) Object is created (new_object() emits object_created(obj)).
|
2) Object is created (app_obj.new_object() emits object_created(obj)).
|
||||||
on_object_created() adds (i) object to collection and emits
|
on_object_created() adds (i) object to collection and emits
|
||||||
(ii) new_object_available() then calls (iii) object.plot()
|
(ii) app_obj.new_object_available() then calls (iii) object.plot()
|
||||||
3) object.plot() creates axes if necessary on
|
3) object.plot() creates axes if necessary on
|
||||||
app.collection.figure. Then plots on it.
|
app.collection.figure. Then plots on it.
|
||||||
4) Plots on a cache-size canvas (in background).
|
4) Plots on a cache-size canvas (in background).
|
||||||
@@ -113,7 +115,7 @@ class CanvasCache(QtCore.QObject):
|
|||||||
|
|
||||||
# Continue to update the cache.
|
# Continue to update the cache.
|
||||||
|
|
||||||
# def on_new_object_available(self):
|
# def on_app_obj.new_object_available(self):
|
||||||
#
|
#
|
||||||
# log.debug("A new object is available. Should plot it!")
|
# log.debug("A new object is available. Should plot it!")
|
||||||
|
|
||||||
@@ -147,9 +149,13 @@ class PlotCanvasLegacy(QtCore.QObject):
|
|||||||
if self.app.defaults['global_theme'] == 'white':
|
if self.app.defaults['global_theme'] == 'white':
|
||||||
theme_color = '#FFFFFF'
|
theme_color = '#FFFFFF'
|
||||||
tick_color = '#000000'
|
tick_color = '#000000'
|
||||||
|
self.rect_hud_color = '#0000FF10'
|
||||||
|
self.text_hud_color = '#000000'
|
||||||
else:
|
else:
|
||||||
theme_color = '#000000'
|
theme_color = '#000000'
|
||||||
tick_color = '#FFFFFF'
|
tick_color = '#FFFFFF'
|
||||||
|
self.rect_hud_color = '#80808040'
|
||||||
|
self.text_hud_color = '#FFFFFF'
|
||||||
|
|
||||||
# workspace lines; I didn't use the rectangle because I didn't want to add another VisPy Node,
|
# workspace lines; I didn't use the rectangle because I didn't want to add another VisPy Node,
|
||||||
# which might decrease performance
|
# which might decrease performance
|
||||||
@@ -295,14 +301,163 @@ class PlotCanvasLegacy(QtCore.QObject):
|
|||||||
# signal is the mouse is dragging
|
# signal is the mouse is dragging
|
||||||
self.is_dragging = False
|
self.is_dragging = False
|
||||||
|
|
||||||
|
self.mouse_press_pos = None
|
||||||
|
|
||||||
# signal if there is a doubleclick
|
# signal if there is a doubleclick
|
||||||
self.is_dblclk = False
|
self.is_dblclk = False
|
||||||
|
|
||||||
|
self.hud_enabled = False
|
||||||
|
self.text_hud = self.Thud(plotcanvas=self)
|
||||||
|
|
||||||
|
# enable Grid lines
|
||||||
|
self.grid_lines_enabled = True
|
||||||
|
|
||||||
# draw a rectangle made out of 4 lines on the canvas to serve as a hint for the work area
|
# draw a rectangle made out of 4 lines on the canvas to serve as a hint for the work area
|
||||||
# all CNC have a limited workspace
|
# all CNC have a limited workspace
|
||||||
if self.app.defaults['global_workspace'] is True:
|
if self.app.defaults['global_workspace'] is True:
|
||||||
self.draw_workspace(workspace_size=self.app.defaults["global_workspaceT"])
|
self.draw_workspace(workspace_size=self.app.defaults["global_workspaceT"])
|
||||||
|
|
||||||
|
if self.app.defaults['global_hud'] is True:
|
||||||
|
self.on_toggle_hud(state=True)
|
||||||
|
|
||||||
|
# Axis Display
|
||||||
|
self.axis_enabled = True
|
||||||
|
|
||||||
|
# enable Axis
|
||||||
|
self.on_toggle_axis(state=True)
|
||||||
|
|
||||||
|
def on_toggle_axis(self, signal=None, state=None):
|
||||||
|
if state is None:
|
||||||
|
state = not self.axis_enabled
|
||||||
|
|
||||||
|
if state:
|
||||||
|
self.axis_enabled = True
|
||||||
|
if self.h_line not in self.axes.lines and self.v_line not in self.axes.lines:
|
||||||
|
self.h_line = self.axes.axhline(color=(0.70, 0.3, 0.3), linewidth=2)
|
||||||
|
self.v_line = self.axes.axvline(color=(0.70, 0.3, 0.3), linewidth=2)
|
||||||
|
self.app.ui.axis_status_label.setStyleSheet("""
|
||||||
|
QLabel
|
||||||
|
{
|
||||||
|
color: black;
|
||||||
|
background-color: peachpuff;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
self.app.inform[str, bool].emit(_("Axis enabled."), False)
|
||||||
|
else:
|
||||||
|
self.axis_enabled = False
|
||||||
|
if self.h_line in self.axes.lines and self.v_line in self.axes.lines:
|
||||||
|
self.axes.lines.remove(self.h_line)
|
||||||
|
self.axes.lines.remove(self.v_line)
|
||||||
|
self.app.ui.axis_status_label.setStyleSheet("")
|
||||||
|
self.app.inform[str, bool].emit(_("Axis disabled."), False)
|
||||||
|
|
||||||
|
self.canvas.draw()
|
||||||
|
|
||||||
|
def on_toggle_hud(self, signal=None, state=None):
|
||||||
|
if state is None:
|
||||||
|
state = not self.hud_enabled
|
||||||
|
|
||||||
|
if state:
|
||||||
|
self.hud_enabled = True
|
||||||
|
self.text_hud.add_artist()
|
||||||
|
self.app.defaults['global_hud'] = True
|
||||||
|
|
||||||
|
self.app.ui.hud_label.setStyleSheet("""
|
||||||
|
QLabel
|
||||||
|
{
|
||||||
|
color: black;
|
||||||
|
background-color: lightblue;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
self.app.inform[str, bool].emit(_("HUD enabled."), False)
|
||||||
|
else:
|
||||||
|
self.hud_enabled = False
|
||||||
|
self.text_hud.remove_artist()
|
||||||
|
self.app.defaults['global_hud'] = False
|
||||||
|
self.app.ui.hud_label.setStyleSheet("")
|
||||||
|
self.app.inform[str, bool].emit(_("HUD disabled."), False)
|
||||||
|
|
||||||
|
self.canvas.draw()
|
||||||
|
|
||||||
|
class Thud(QtCore.QObject):
|
||||||
|
text_changed = QtCore.pyqtSignal(str)
|
||||||
|
|
||||||
|
def __init__(self, plotcanvas):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.p = plotcanvas
|
||||||
|
units = self.p.app.defaults['units']
|
||||||
|
self._text = 'Dx: %s [%s]\nDy: %s [%s]\n\nX: %s [%s]\nY: %s [%s]' % \
|
||||||
|
('0.0000', units, '0.0000', units, '0.0000', units, '0.0000', units)
|
||||||
|
|
||||||
|
# set font size
|
||||||
|
qsettings = QtCore.QSettings("Open Source", "FlatCAM")
|
||||||
|
if qsettings.contains("hud_font_size"):
|
||||||
|
# I multiply with 2.5 because this seems to be the difference between the value taken by the VisPy (3D)
|
||||||
|
# and Matplotlib (Legacy2D FlatCAM graphic engine)
|
||||||
|
fsize = int(qsettings.value('hud_font_size', type=int) * 2.5)
|
||||||
|
else:
|
||||||
|
fsize = 20
|
||||||
|
|
||||||
|
self.hud_holder = AnchoredText(self._text, prop=dict(size=fsize), frameon=True, loc='upper left')
|
||||||
|
self.hud_holder.patch.set_boxstyle("round,pad=0.,rounding_size=0.2")
|
||||||
|
|
||||||
|
fc_color = self.p.rect_hud_color[:-2]
|
||||||
|
fc_alpha = int(self.p.rect_hud_color[-2:], 16) / 255
|
||||||
|
text_color = self.p.text_hud_color
|
||||||
|
|
||||||
|
self.hud_holder.patch.set_facecolor(fc_color)
|
||||||
|
self.hud_holder.patch.set_alpha(fc_alpha)
|
||||||
|
self.hud_holder.patch.set_edgecolor((0, 0, 0, 0))
|
||||||
|
|
||||||
|
self. hud_holder.txt._text.set_color(color=text_color)
|
||||||
|
self.text_changed.connect(self.on_text_changed)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def text(self):
|
||||||
|
return self._text
|
||||||
|
|
||||||
|
@text.setter
|
||||||
|
def text(self, val):
|
||||||
|
self.text_changed.emit(val)
|
||||||
|
self._text = val
|
||||||
|
|
||||||
|
def on_text_changed(self, txt):
|
||||||
|
try:
|
||||||
|
txt = txt.replace('\t', ' ')
|
||||||
|
self.hud_holder.txt.set_text(txt)
|
||||||
|
self.p.canvas.draw()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def add_artist(self):
|
||||||
|
if self.hud_holder not in self.p.axes.artists:
|
||||||
|
self.p.axes.add_artist(self.hud_holder)
|
||||||
|
|
||||||
|
def remove_artist(self):
|
||||||
|
if self.hud_holder in self.p.axes.artists:
|
||||||
|
self.p.axes.artists.remove(self.hud_holder)
|
||||||
|
|
||||||
|
def on_toggle_grid_lines(self):
|
||||||
|
state = not self.grid_lines_enabled
|
||||||
|
|
||||||
|
if state:
|
||||||
|
self.grid_lines_enabled = True
|
||||||
|
self.axes.grid(True)
|
||||||
|
try:
|
||||||
|
self.canvas.draw()
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
self.app.inform[str, bool].emit(_("Grid enabled."), False)
|
||||||
|
else:
|
||||||
|
self.grid_lines_enabled = False
|
||||||
|
self.axes.grid(False)
|
||||||
|
try:
|
||||||
|
self.canvas.draw()
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
self.app.inform[str, bool].emit(_("Grid disabled."), False)
|
||||||
|
|
||||||
def draw_workspace(self, workspace_size):
|
def draw_workspace(self, workspace_size):
|
||||||
"""
|
"""
|
||||||
Draw a rectangular shape on canvas to specify our valid workspace.
|
Draw a rectangular shape on canvas to specify our valid workspace.
|
||||||
@@ -329,12 +484,23 @@ class PlotCanvasLegacy(QtCore.QObject):
|
|||||||
self.axes.add_line(self.workspace_line)
|
self.axes.add_line(self.workspace_line)
|
||||||
self.canvas.draw()
|
self.canvas.draw()
|
||||||
|
|
||||||
|
self.app.ui.wplace_label.set_value(workspace_size[:3])
|
||||||
|
self.app.ui.wplace_label.setToolTip(workspace_size)
|
||||||
|
self.fcapp.ui.wplace_label.setStyleSheet("""
|
||||||
|
QLabel
|
||||||
|
{
|
||||||
|
color: black;
|
||||||
|
background-color: lightgreen;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
def delete_workspace(self):
|
def delete_workspace(self):
|
||||||
try:
|
try:
|
||||||
self.axes.lines.remove(self.workspace_line)
|
self.axes.lines.remove(self.workspace_line)
|
||||||
self.canvas.draw()
|
self.canvas.draw()
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
self.fcapp.ui.wplace_label.setStyleSheet("")
|
||||||
|
|
||||||
def graph_event_connect(self, event_name, callback):
|
def graph_event_connect(self, event_name, callback):
|
||||||
"""
|
"""
|
||||||
@@ -423,7 +589,7 @@ class PlotCanvasLegacy(QtCore.QObject):
|
|||||||
|
|
||||||
if self.big_cursor is False:
|
if self.big_cursor is False:
|
||||||
try:
|
try:
|
||||||
x, y = self.app.geo_editor.snap(x_pos, y_pos)
|
x, y = self.snap(x_pos, y_pos)
|
||||||
|
|
||||||
# Pointer (snapped)
|
# Pointer (snapped)
|
||||||
# The size of the cursor is multiplied by 1.65 because that value made the cursor similar with the
|
# The size of the cursor is multiplied by 1.65 because that value made the cursor similar with the
|
||||||
@@ -781,6 +947,7 @@ class PlotCanvasLegacy(QtCore.QObject):
|
|||||||
def on_mouse_press(self, event):
|
def on_mouse_press(self, event):
|
||||||
|
|
||||||
self.is_dragging = True
|
self.is_dragging = True
|
||||||
|
self.mouse_press_pos = (event.x, event.y)
|
||||||
|
|
||||||
# Check for middle mouse button press
|
# Check for middle mouse button press
|
||||||
if self.app.defaults["global_pan_button"] == '2':
|
if self.app.defaults["global_pan_button"] == '2':
|
||||||
@@ -806,6 +973,10 @@ class PlotCanvasLegacy(QtCore.QObject):
|
|||||||
|
|
||||||
def on_mouse_release(self, event):
|
def on_mouse_release(self, event):
|
||||||
|
|
||||||
|
mouse_release_pos = (event.x, event.y)
|
||||||
|
delta = 0.05
|
||||||
|
|
||||||
|
if abs(self.distance(self.mouse_press_pos, mouse_release_pos)) < delta:
|
||||||
self.is_dragging = False
|
self.is_dragging = False
|
||||||
|
|
||||||
# Check for middle mouse button release to complete pan procedure
|
# Check for middle mouse button release to complete pan procedure
|
||||||
@@ -858,7 +1029,7 @@ class PlotCanvasLegacy(QtCore.QObject):
|
|||||||
self.canvas.draw_idle()
|
self.canvas.draw_idle()
|
||||||
|
|
||||||
# #### Temporary place-holder for cached update #####
|
# #### Temporary place-holder for cached update #####
|
||||||
self.update_screen_request.emit([0, 0, 0, 0, 0])
|
# self.update_screen_request.emit([0, 0, 0, 0, 0])
|
||||||
|
|
||||||
if self.app.defaults["global_cursor_color_enabled"] is True:
|
if self.app.defaults["global_cursor_color_enabled"] is True:
|
||||||
self.draw_cursor(x_pos=x, y_pos=y, color=self.app.cursor_color_3D)
|
self.draw_cursor(x_pos=x, y_pos=y, color=self.app.cursor_color_3D)
|
||||||
@@ -910,6 +1081,59 @@ class PlotCanvasLegacy(QtCore.QObject):
|
|||||||
|
|
||||||
return width / xpx, height / ypx
|
return width / xpx, height / ypx
|
||||||
|
|
||||||
|
def snap(self, x, y):
|
||||||
|
"""
|
||||||
|
Adjusts coordinates to snap settings.
|
||||||
|
|
||||||
|
:param x: Input coordinate X
|
||||||
|
:param y: Input coordinate Y
|
||||||
|
:return: Snapped (x, y)
|
||||||
|
"""
|
||||||
|
|
||||||
|
snap_x, snap_y = (x, y)
|
||||||
|
snap_distance = np.Inf
|
||||||
|
|
||||||
|
# ### Grid snap
|
||||||
|
if self.app.grid_status():
|
||||||
|
if self.app.defaults["global_gridx"] != 0:
|
||||||
|
try:
|
||||||
|
snap_x_ = round(x / float(self.app.defaults["global_gridx"])) * \
|
||||||
|
float(self.app.defaults["global_gridx"])
|
||||||
|
except TypeError:
|
||||||
|
snap_x_ = x
|
||||||
|
else:
|
||||||
|
snap_x_ = x
|
||||||
|
|
||||||
|
# If the Grid_gap_linked on Grid Toolbar is checked then the snap distance on GridY entry will be ignored
|
||||||
|
# and it will use the snap distance from GridX entry
|
||||||
|
if self.app.ui.grid_gap_link_cb.isChecked():
|
||||||
|
if self.app.defaults["global_gridx"] != 0:
|
||||||
|
try:
|
||||||
|
snap_y_ = round(y / float(self.app.defaults["global_gridx"])) * \
|
||||||
|
float(self.app.defaults["global_gridx"])
|
||||||
|
except TypeError:
|
||||||
|
snap_y_ = y
|
||||||
|
else:
|
||||||
|
snap_y_ = y
|
||||||
|
else:
|
||||||
|
if self.app.defaults["global_gridy"] != 0:
|
||||||
|
try:
|
||||||
|
snap_y_ = round(y / float(self.app.defaults["global_gridy"])) * \
|
||||||
|
float(self.app.defaults["global_gridy"])
|
||||||
|
except TypeError:
|
||||||
|
snap_y_ = y
|
||||||
|
else:
|
||||||
|
snap_y_ = y
|
||||||
|
nearest_grid_distance = self.distance((x, y), (snap_x_, snap_y_))
|
||||||
|
if nearest_grid_distance < snap_distance:
|
||||||
|
snap_x, snap_y = (snap_x_, snap_y_)
|
||||||
|
|
||||||
|
return snap_x, snap_y
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def distance(pt1, pt2):
|
||||||
|
return np.sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2)
|
||||||
|
|
||||||
|
|
||||||
class FakeCursor(QtCore.QObject):
|
class FakeCursor(QtCore.QObject):
|
||||||
"""
|
"""
|
||||||
@@ -13,6 +13,7 @@ import numpy as np
|
|||||||
|
|
||||||
import vispy.scene as scene
|
import vispy.scene as scene
|
||||||
from vispy.scene.cameras.base_camera import BaseCamera
|
from vispy.scene.cameras.base_camera import BaseCamera
|
||||||
|
# from vispy.scene.widgets import Widget as VisPyWidget
|
||||||
from vispy.color import Color
|
from vispy.color import Color
|
||||||
|
|
||||||
import time
|
import time
|
||||||
@@ -13,7 +13,7 @@ from vispy.color import Color
|
|||||||
from shapely.geometry import Polygon, LineString, LinearRing
|
from shapely.geometry import Polygon, LineString, LinearRing
|
||||||
import threading
|
import threading
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from flatcamGUI.VisPyTesselators import GLUTess
|
from AppGUI.VisPyTesselators import GLUTess
|
||||||
|
|
||||||
|
|
||||||
class FlatCAMLineVisual(LineVisual):
|
class FlatCAMLineVisual(LineVisual):
|
||||||
@@ -5,7 +5,7 @@ from defaults import FlatCAMDefaults
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -29,7 +29,7 @@ class PreferencesUIManager:
|
|||||||
|
|
||||||
:param defaults: a dictionary storage where all the application settings are stored
|
:param defaults: a dictionary storage where all the application settings are stored
|
||||||
:param data_path: a path to the file where all the preferences are stored for persistence
|
:param data_path: a path to the file where all the preferences are stored for persistence
|
||||||
:param ui: reference to the FlatCAMGUI class which constructs the UI
|
:param ui: reference to the MainGUI class which constructs the UI
|
||||||
:param inform: a pyqtSignal used to display information's in the StatusBar of the GUI
|
:param inform: a pyqtSignal used to display information's in the StatusBar of the GUI
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ class PreferencesUIManager:
|
|||||||
self.preferences_changed_flag = False
|
self.preferences_changed_flag = False
|
||||||
|
|
||||||
# when adding entries here read the comments in the method found below named:
|
# when adding entries here read the comments in the method found below named:
|
||||||
# def new_object(self, kind, name, initialize, active=True, fit=True, plot=True)
|
# def app_obj.new_object(self, kind, name, initialize, active=True, fit=True, plot=True)
|
||||||
self.defaults_form_fields = {
|
self.defaults_form_fields = {
|
||||||
# General App
|
# General App
|
||||||
"decimals_inch": self.ui.general_defaults_form.general_app_group.precision_inch_entry,
|
"decimals_inch": self.ui.general_defaults_form.general_app_group.precision_inch_entry,
|
||||||
@@ -127,12 +127,6 @@ class PreferencesUIManager:
|
|||||||
"gerber_plot_line": self.ui.gerber_defaults_form.gerber_gen_group.pl_color_entry,
|
"gerber_plot_line": self.ui.gerber_defaults_form.gerber_gen_group.pl_color_entry,
|
||||||
|
|
||||||
# Gerber Options
|
# Gerber Options
|
||||||
"gerber_isotooldia": self.ui.gerber_defaults_form.gerber_opt_group.iso_tool_dia_entry,
|
|
||||||
"gerber_isopasses": self.ui.gerber_defaults_form.gerber_opt_group.iso_width_entry,
|
|
||||||
"gerber_isooverlap": self.ui.gerber_defaults_form.gerber_opt_group.iso_overlap_entry,
|
|
||||||
"gerber_combine_passes": self.ui.gerber_defaults_form.gerber_opt_group.combine_passes_cb,
|
|
||||||
"gerber_iso_scope": self.ui.gerber_defaults_form.gerber_opt_group.iso_scope_radio,
|
|
||||||
"gerber_milling_type": self.ui.gerber_defaults_form.gerber_opt_group.milling_type_radio,
|
|
||||||
"gerber_noncoppermargin": self.ui.gerber_defaults_form.gerber_opt_group.noncopper_margin_entry,
|
"gerber_noncoppermargin": self.ui.gerber_defaults_form.gerber_opt_group.noncopper_margin_entry,
|
||||||
"gerber_noncopperrounded": self.ui.gerber_defaults_form.gerber_opt_group.noncopper_rounded_cb,
|
"gerber_noncopperrounded": self.ui.gerber_defaults_form.gerber_opt_group.noncopper_rounded_cb,
|
||||||
"gerber_bboxmargin": self.ui.gerber_defaults_form.gerber_opt_group.bbmargin_entry,
|
"gerber_bboxmargin": self.ui.gerber_defaults_form.gerber_opt_group.bbmargin_entry,
|
||||||
@@ -143,12 +137,6 @@ class PreferencesUIManager:
|
|||||||
# "gerber_aperture_scale_factor": self.ui.gerber_defaults_form.gerber_adv_opt_group.scale_aperture_entry,
|
# "gerber_aperture_scale_factor": self.ui.gerber_defaults_form.gerber_adv_opt_group.scale_aperture_entry,
|
||||||
# "gerber_aperture_buffer_factor": self.ui.gerber_defaults_form.gerber_adv_opt_group.buffer_aperture_entry,
|
# "gerber_aperture_buffer_factor": self.ui.gerber_defaults_form.gerber_adv_opt_group.buffer_aperture_entry,
|
||||||
"gerber_follow": self.ui.gerber_defaults_form.gerber_adv_opt_group.follow_cb,
|
"gerber_follow": self.ui.gerber_defaults_form.gerber_adv_opt_group.follow_cb,
|
||||||
"gerber_tool_type": self.ui.gerber_defaults_form.gerber_adv_opt_group.tool_type_radio,
|
|
||||||
"gerber_vtipdia": self.ui.gerber_defaults_form.gerber_adv_opt_group.tipdia_spinner,
|
|
||||||
"gerber_vtipangle": self.ui.gerber_defaults_form.gerber_adv_opt_group.tipangle_spinner,
|
|
||||||
"gerber_vcutz": self.ui.gerber_defaults_form.gerber_adv_opt_group.cutz_spinner,
|
|
||||||
"gerber_iso_type": self.ui.gerber_defaults_form.gerber_adv_opt_group.iso_type_radio,
|
|
||||||
|
|
||||||
"gerber_buffering": self.ui.gerber_defaults_form.gerber_adv_opt_group.buffering_radio,
|
"gerber_buffering": self.ui.gerber_defaults_form.gerber_adv_opt_group.buffering_radio,
|
||||||
"gerber_simplification": self.ui.gerber_defaults_form.gerber_adv_opt_group.simplify_cb,
|
"gerber_simplification": self.ui.gerber_defaults_form.gerber_adv_opt_group.simplify_cb,
|
||||||
"gerber_simp_tolerance": self.ui.gerber_defaults_form.gerber_adv_opt_group.simplification_tol_spinner,
|
"gerber_simp_tolerance": self.ui.gerber_defaults_form.gerber_adv_opt_group.simplification_tol_spinner,
|
||||||
@@ -180,6 +168,7 @@ class PreferencesUIManager:
|
|||||||
# Excellon General
|
# Excellon General
|
||||||
"excellon_plot": self.ui.excellon_defaults_form.excellon_gen_group.plot_cb,
|
"excellon_plot": self.ui.excellon_defaults_form.excellon_gen_group.plot_cb,
|
||||||
"excellon_solid": self.ui.excellon_defaults_form.excellon_gen_group.solid_cb,
|
"excellon_solid": self.ui.excellon_defaults_form.excellon_gen_group.solid_cb,
|
||||||
|
"excellon_multicolored": self.ui.excellon_defaults_form.excellon_gen_group.multicolored_cb,
|
||||||
"excellon_format_upper_in":
|
"excellon_format_upper_in":
|
||||||
self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_upper_in_entry,
|
self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_upper_in_entry,
|
||||||
"excellon_format_lower_in":
|
"excellon_format_lower_in":
|
||||||
@@ -271,6 +260,7 @@ class PreferencesUIManager:
|
|||||||
|
|
||||||
# Geometry General
|
# Geometry General
|
||||||
"geometry_plot": self.ui.geometry_defaults_form.geometry_gen_group.plot_cb,
|
"geometry_plot": self.ui.geometry_defaults_form.geometry_gen_group.plot_cb,
|
||||||
|
"geometry_multicolored": self.ui.geometry_defaults_form.geometry_gen_group.multicolored_cb,
|
||||||
"geometry_circle_steps": self.ui.geometry_defaults_form.geometry_gen_group.circle_steps_entry,
|
"geometry_circle_steps": self.ui.geometry_defaults_form.geometry_gen_group.circle_steps_entry,
|
||||||
"geometry_cnctooldia": self.ui.geometry_defaults_form.geometry_gen_group.cnctooldia_entry,
|
"geometry_cnctooldia": self.ui.geometry_defaults_form.geometry_gen_group.cnctooldia_entry,
|
||||||
"geometry_plot_line": self.ui.geometry_defaults_form.geometry_gen_group.line_color_entry,
|
"geometry_plot_line": self.ui.geometry_defaults_form.geometry_gen_group.line_color_entry,
|
||||||
@@ -338,6 +328,28 @@ class PreferencesUIManager:
|
|||||||
"cncjob_annotation_fontsize": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontsize_sp,
|
"cncjob_annotation_fontsize": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontsize_sp,
|
||||||
"cncjob_annotation_fontcolor": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontcolor_entry,
|
"cncjob_annotation_fontcolor": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontcolor_entry,
|
||||||
|
|
||||||
|
# Isolation Routing Tool
|
||||||
|
"tools_iso_tooldia": self.ui.tools_defaults_form.tools_iso_group.tool_dia_entry,
|
||||||
|
"tools_iso_order": self.ui.tools_defaults_form.tools_iso_group.order_radio,
|
||||||
|
"tools_iso_tool_type": self.ui.tools_defaults_form.tools_iso_group.tool_type_radio,
|
||||||
|
"tools_iso_tool_vtipdia": self.ui.tools_defaults_form.tools_iso_group.tipdia_entry,
|
||||||
|
"tools_iso_tool_vtipangle": self.ui.tools_defaults_form.tools_iso_group.tipangle_entry,
|
||||||
|
"tools_iso_tool_cutz": self.ui.tools_defaults_form.tools_iso_group.cutz_entry,
|
||||||
|
"tools_iso_newdia": self.ui.tools_defaults_form.tools_iso_group.newdia_entry,
|
||||||
|
|
||||||
|
"tools_iso_passes": self.ui.tools_defaults_form.tools_iso_group.passes_entry,
|
||||||
|
"tools_iso_overlap": self.ui.tools_defaults_form.tools_iso_group.overlap_entry,
|
||||||
|
"tools_iso_milling_type": self.ui.tools_defaults_form.tools_iso_group.milling_type_radio,
|
||||||
|
"tools_iso_follow": self.ui.tools_defaults_form.tools_iso_group.follow_cb,
|
||||||
|
"tools_iso_isotype": self.ui.tools_defaults_form.tools_iso_group.iso_type_radio,
|
||||||
|
|
||||||
|
"tools_iso_rest": self.ui.tools_defaults_form.tools_iso_group.rest_cb,
|
||||||
|
"tools_iso_combine_passes": self.ui.tools_defaults_form.tools_iso_group.combine_passes_cb,
|
||||||
|
"tools_iso_isoexcept": self.ui.tools_defaults_form.tools_iso_group.except_cb,
|
||||||
|
"tools_iso_selection": self.ui.tools_defaults_form.tools_iso_group.select_combo,
|
||||||
|
"tools_iso_area_shape": self.ui.tools_defaults_form.tools_iso_group.area_shape_radio,
|
||||||
|
"tools_iso_plotting": self.ui.tools_defaults_form.tools_iso_group.plotting_radio,
|
||||||
|
|
||||||
# NCC Tool
|
# NCC Tool
|
||||||
"tools_ncctools": self.ui.tools_defaults_form.tools_ncc_group.ncc_tool_dia_entry,
|
"tools_ncctools": self.ui.tools_defaults_form.tools_ncc_group.ncc_tool_dia_entry,
|
||||||
"tools_nccorder": self.ui.tools_defaults_form.tools_ncc_group.ncc_order_radio,
|
"tools_nccorder": self.ui.tools_defaults_form.tools_ncc_group.ncc_order_radio,
|
||||||
@@ -351,13 +363,13 @@ class PreferencesUIManager:
|
|||||||
"tools_ncc_offset_value": self.ui.tools_defaults_form.tools_ncc_group.ncc_offset_spinner,
|
"tools_ncc_offset_value": self.ui.tools_defaults_form.tools_ncc_group.ncc_offset_spinner,
|
||||||
"tools_nccref": self.ui.tools_defaults_form.tools_ncc_group.select_combo,
|
"tools_nccref": self.ui.tools_defaults_form.tools_ncc_group.select_combo,
|
||||||
"tools_ncc_area_shape": self.ui.tools_defaults_form.tools_ncc_group.area_shape_radio,
|
"tools_ncc_area_shape": self.ui.tools_defaults_form.tools_ncc_group.area_shape_radio,
|
||||||
"tools_ncc_plotting": self.ui.tools_defaults_form.tools_ncc_group.ncc_plotting_radio,
|
|
||||||
"tools_nccmilling_type": self.ui.tools_defaults_form.tools_ncc_group.milling_type_radio,
|
"tools_nccmilling_type": self.ui.tools_defaults_form.tools_ncc_group.milling_type_radio,
|
||||||
"tools_ncctool_type": self.ui.tools_defaults_form.tools_ncc_group.tool_type_radio,
|
"tools_ncctool_type": self.ui.tools_defaults_form.tools_ncc_group.tool_type_radio,
|
||||||
"tools_ncccutz": self.ui.tools_defaults_form.tools_ncc_group.cutz_entry,
|
"tools_ncccutz": self.ui.tools_defaults_form.tools_ncc_group.cutz_entry,
|
||||||
"tools_ncctipdia": self.ui.tools_defaults_form.tools_ncc_group.tipdia_entry,
|
"tools_ncctipdia": self.ui.tools_defaults_form.tools_ncc_group.tipdia_entry,
|
||||||
"tools_ncctipangle": self.ui.tools_defaults_form.tools_ncc_group.tipangle_entry,
|
"tools_ncctipangle": self.ui.tools_defaults_form.tools_ncc_group.tipangle_entry,
|
||||||
"tools_nccnewdia": self.ui.tools_defaults_form.tools_ncc_group.newdia_entry,
|
"tools_nccnewdia": self.ui.tools_defaults_form.tools_ncc_group.newdia_entry,
|
||||||
|
"tools_ncc_plotting": self.ui.tools_defaults_form.tools_ncc_group.plotting_radio,
|
||||||
|
|
||||||
# CutOut Tool
|
# CutOut Tool
|
||||||
"tools_cutouttooldia": self.ui.tools_defaults_form.tools_cutout_group.cutout_tooldia_entry,
|
"tools_cutouttooldia": self.ui.tools_defaults_form.tools_cutout_group.cutout_tooldia_entry,
|
||||||
@@ -467,6 +479,12 @@ class PreferencesUIManager:
|
|||||||
"tools_solderpaste_pp": self.ui.tools_defaults_form.tools_solderpaste_group.pp_combo,
|
"tools_solderpaste_pp": self.ui.tools_defaults_form.tools_solderpaste_group.pp_combo,
|
||||||
"tools_sub_close_paths": self.ui.tools_defaults_form.tools_sub_group.close_paths_cb,
|
"tools_sub_close_paths": self.ui.tools_defaults_form.tools_sub_group.close_paths_cb,
|
||||||
|
|
||||||
|
# Corner Markers Tool
|
||||||
|
|
||||||
|
"tools_corners_thickness": self.ui.tools_defaults_form.tools_corners_group.thick_entry,
|
||||||
|
"tools_corners_length": self.ui.tools_defaults_form.tools_corners_group.l_entry,
|
||||||
|
"tools_corners_margin": self.ui.tools_defaults_form.tools_corners_group.margin_entry,
|
||||||
|
|
||||||
# #######################################################################################################
|
# #######################################################################################################
|
||||||
# ########################################## TOOLS 2 ####################################################
|
# ########################################## TOOLS 2 ####################################################
|
||||||
# #######################################################################################################
|
# #######################################################################################################
|
||||||
@@ -650,7 +668,7 @@ class PreferencesUIManager:
|
|||||||
|
|
||||||
def show_preferences_gui(self):
|
def show_preferences_gui(self):
|
||||||
"""
|
"""
|
||||||
Called to initialize and show the Preferences GUI
|
Called to initialize and show the Preferences AppGUI
|
||||||
|
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
@@ -913,7 +931,51 @@ class PreferencesUIManager:
|
|||||||
# make sure we update the self.current_defaults dict used to undo changes to self.defaults
|
# make sure we update the self.current_defaults dict used to undo changes to self.defaults
|
||||||
self.defaults.current_defaults.update(self.defaults)
|
self.defaults.current_defaults.update(self.defaults)
|
||||||
|
|
||||||
if save_to_file:
|
# deal with theme change
|
||||||
|
theme_settings = QtCore.QSettings("Open Source", "FlatCAM")
|
||||||
|
if theme_settings.contains("theme"):
|
||||||
|
theme = theme_settings.value('theme', type=str)
|
||||||
|
else:
|
||||||
|
theme = 'white'
|
||||||
|
|
||||||
|
should_restart = False
|
||||||
|
theme_new_val = self.ui.general_defaults_form.general_gui_group.theme_radio.get_value()
|
||||||
|
|
||||||
|
ge = self.defaults["global_graphic_engine"]
|
||||||
|
ge_val = self.ui.general_defaults_form.general_app_group.ge_radio.get_value()
|
||||||
|
|
||||||
|
if theme_new_val != theme or ge != ge_val:
|
||||||
|
msgbox = QtWidgets.QMessageBox()
|
||||||
|
msgbox.setText(_("Are you sure you want to continue?"))
|
||||||
|
msgbox.setWindowTitle(_("Application restart"))
|
||||||
|
msgbox.setWindowIcon(QtGui.QIcon(self.ui.app.resource_location + '/warning.png'))
|
||||||
|
msgbox.setIcon(QtWidgets.QMessageBox.Question)
|
||||||
|
|
||||||
|
bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole)
|
||||||
|
msgbox.addButton(_('Cancel'), QtWidgets.QMessageBox.NoRole)
|
||||||
|
|
||||||
|
msgbox.setDefaultButton(bt_yes)
|
||||||
|
msgbox.exec_()
|
||||||
|
response = msgbox.clickedButton()
|
||||||
|
|
||||||
|
if theme_new_val != theme:
|
||||||
|
if response == bt_yes:
|
||||||
|
theme_settings.setValue('theme', theme_new_val)
|
||||||
|
|
||||||
|
# This will write the setting to the platform specific storage.
|
||||||
|
del theme_settings
|
||||||
|
|
||||||
|
should_restart = True
|
||||||
|
else:
|
||||||
|
self.ui.general_defaults_form.general_gui_group.theme_radio.set_value(theme)
|
||||||
|
else:
|
||||||
|
if response == bt_yes:
|
||||||
|
self.defaults["global_graphic_engine"] = ge_val
|
||||||
|
should_restart = True
|
||||||
|
else:
|
||||||
|
self.ui.general_defaults_form.general_app_group.ge_radio.set_value(ge)
|
||||||
|
|
||||||
|
if save_to_file or should_restart is True:
|
||||||
self.save_defaults(silent=False)
|
self.save_defaults(silent=False)
|
||||||
# load the defaults so they are updated into the app
|
# load the defaults so they are updated into the app
|
||||||
self.defaults.load(filename=os.path.join(self.data_path, 'current_defaults.FlatConfig'))
|
self.defaults.load(filename=os.path.join(self.data_path, 'current_defaults.FlatConfig'))
|
||||||
@@ -935,6 +997,10 @@ class PreferencesUIManager:
|
|||||||
tb_fsize = self.ui.general_defaults_form.general_app_set_group.textbox_font_size_spinner.get_value()
|
tb_fsize = self.ui.general_defaults_form.general_app_set_group.textbox_font_size_spinner.get_value()
|
||||||
settgs.setValue('textbox_font_size', tb_fsize)
|
settgs.setValue('textbox_font_size', tb_fsize)
|
||||||
|
|
||||||
|
# save the HUD font size
|
||||||
|
hud_fsize = self.ui.general_defaults_form.general_app_set_group.hud_font_size_spinner.get_value()
|
||||||
|
settgs.setValue('hud_font_size', hud_fsize)
|
||||||
|
|
||||||
settgs.setValue(
|
settgs.setValue(
|
||||||
'machinist',
|
'machinist',
|
||||||
1 if self.ui.general_defaults_form.general_app_set_group.machinist_cb.get_value() else 0
|
1 if self.ui.general_defaults_form.general_app_set_group.machinist_cb.get_value() else 0
|
||||||
@@ -951,6 +1017,9 @@ class PreferencesUIManager:
|
|||||||
self.ui.plot_tab_area.closeTab(idx)
|
self.ui.plot_tab_area.closeTab(idx)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
if should_restart is True:
|
||||||
|
self.ui.app.on_app_restart()
|
||||||
|
|
||||||
def on_pref_close_button(self):
|
def on_pref_close_button(self):
|
||||||
# Preferences saved, update flag
|
# Preferences saved, update flag
|
||||||
self.preferences_changed_flag = False
|
self.preferences_changed_flag = False
|
||||||
@@ -1000,6 +1069,7 @@ class PreferencesUIManager:
|
|||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
self.defaults.report_usage("save_defaults")
|
self.defaults.report_usage("save_defaults")
|
||||||
|
log.debug("App.PreferencesUIManager.save_defaults()")
|
||||||
|
|
||||||
if data_path is None:
|
if data_path is None:
|
||||||
data_path = self.data_path
|
data_path = self.data_path
|
||||||
@@ -1036,7 +1106,7 @@ class PreferencesUIManager:
|
|||||||
if self.ui.toolbarfile.isVisible():
|
if self.ui.toolbarfile.isVisible():
|
||||||
tb_status += 1
|
tb_status += 1
|
||||||
|
|
||||||
if self.ui.toolbargeo.isVisible():
|
if self.ui.toolbaredit.isVisible():
|
||||||
tb_status += 2
|
tb_status += 2
|
||||||
|
|
||||||
if self.ui.toolbarview.isVisible():
|
if self.ui.toolbarview.isVisible():
|
||||||
@@ -1054,7 +1124,7 @@ class PreferencesUIManager:
|
|||||||
if self.ui.grb_edit_toolbar.isVisible():
|
if self.ui.grb_edit_toolbar.isVisible():
|
||||||
tb_status += 64
|
tb_status += 64
|
||||||
|
|
||||||
if self.ui.snap_toolbar.isVisible():
|
if self.ui.status_toolbar.isVisible():
|
||||||
tb_status += 128
|
tb_status += 128
|
||||||
|
|
||||||
if self.ui.toolbarshell.isVisible():
|
if self.ui.toolbarshell.isVisible():
|
||||||
@@ -1118,6 +1188,7 @@ class PreferencesUIManager:
|
|||||||
"Do you want to save the Preferences?"))
|
"Do you want to save the Preferences?"))
|
||||||
msgbox.setWindowTitle(_("Save Preferences"))
|
msgbox.setWindowTitle(_("Save Preferences"))
|
||||||
msgbox.setWindowIcon(QtGui.QIcon(self.ui.app.resource_location + '/save_as.png'))
|
msgbox.setWindowIcon(QtGui.QIcon(self.ui.app.resource_location + '/save_as.png'))
|
||||||
|
msgbox.setIcon(QtWidgets.QMessageBox.Question)
|
||||||
|
|
||||||
bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole)
|
bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole)
|
||||||
msgbox.addButton(_('No'), QtWidgets.QMessageBox.NoRole)
|
msgbox.addButton(_('No'), QtWidgets.QMessageBox.NoRole)
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from flatcamGUI.GUIElements import *
|
from AppGUI.GUIElements import *
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
|
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
from PyQt5 import QtWidgets, QtGui, QtCore
|
from PyQt5 import QtWidgets, QtGui, QtCore
|
||||||
from PyQt5.QtCore import QSettings, Qt
|
from PyQt5.QtCore import QSettings, Qt
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCTextArea, FCCheckBox, FCComboBox, FCSpinner, FCEntry
|
from AppGUI.GUIElements import FCTextArea, FCCheckBox, FCComboBox, FCSpinner, FCEntry
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
from PyQt5 import QtWidgets, QtCore, QtGui
|
from PyQt5 import QtWidgets, QtCore, QtGui
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCCheckBox, RadioSet, FCSpinner, FCDoubleSpinner, FCEntry
|
from AppGUI.GUIElements import FCCheckBox, RadioSet, FCSpinner, FCDoubleSpinner, FCEntry
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets, QtGui
|
from PyQt5 import QtWidgets, QtGui
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCTextArea
|
from AppGUI.GUIElements import FCTextArea
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
|
|
||||||
from flatcamGUI.preferences.cncjob.CNCJobAdvOptPrefGroupUI import CNCJobAdvOptPrefGroupUI
|
from AppGUI.preferences.cncjob.CNCJobAdvOptPrefGroupUI import CNCJobAdvOptPrefGroupUI
|
||||||
from flatcamGUI.preferences.cncjob.CNCJobOptPrefGroupUI import CNCJobOptPrefGroupUI
|
from AppGUI.preferences.cncjob.CNCJobOptPrefGroupUI import CNCJobOptPrefGroupUI
|
||||||
from flatcamGUI.preferences.cncjob.CNCJobGenPrefGroupUI import CNCJobGenPrefGroupUI
|
from AppGUI.preferences.cncjob.CNCJobGenPrefGroupUI import CNCJobGenPrefGroupUI
|
||||||
|
|
||||||
|
|
||||||
class CNCJobPreferencesUI(QtWidgets.QWidget):
|
class CNCJobPreferencesUI(QtWidgets.QWidget):
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCDoubleSpinner, FCEntry, FloatEntry, RadioSet, FCCheckBox
|
from AppGUI.GUIElements import FCDoubleSpinner, RadioSet, FCCheckBox, NumericalEvalTupleEntry, NumericalEvalEntry
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -60,7 +60,7 @@ class ExcellonAdvOptPrefGroupUI(OptionsGroupUI):
|
|||||||
toolchange_xy_label.setToolTip(
|
toolchange_xy_label.setToolTip(
|
||||||
_("Toolchange X,Y position.")
|
_("Toolchange X,Y position.")
|
||||||
)
|
)
|
||||||
self.toolchangexy_entry = FCEntry()
|
self.toolchangexy_entry = NumericalEvalTupleEntry(border_color='#0069A9')
|
||||||
|
|
||||||
grid1.addWidget(toolchange_xy_label, 1, 0)
|
grid1.addWidget(toolchange_xy_label, 1, 0)
|
||||||
grid1.addWidget(self.toolchangexy_entry, 1, 1)
|
grid1.addWidget(self.toolchangexy_entry, 1, 1)
|
||||||
@@ -71,7 +71,7 @@ class ExcellonAdvOptPrefGroupUI(OptionsGroupUI):
|
|||||||
_("Height of the tool just after start.\n"
|
_("Height of the tool just after start.\n"
|
||||||
"Delete the value if you don't need this feature.")
|
"Delete the value if you don't need this feature.")
|
||||||
)
|
)
|
||||||
self.estartz_entry = FloatEntry()
|
self.estartz_entry = NumericalEvalEntry(border_color='#0069A9')
|
||||||
|
|
||||||
grid1.addWidget(startzlabel, 2, 0)
|
grid1.addWidget(startzlabel, 2, 0)
|
||||||
grid1.addWidget(self.estartz_entry, 2, 1)
|
grid1.addWidget(self.estartz_entry, 2, 1)
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCSpinner, FCDoubleSpinner, RadioSet
|
from AppGUI.GUIElements import FCSpinner, FCDoubleSpinner, RadioSet
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
from PyQt5 import QtWidgets, QtCore
|
from PyQt5 import QtWidgets, QtCore
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import RadioSet, FCSpinner
|
from AppGUI.GUIElements import RadioSet, FCSpinner
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -3,10 +3,10 @@ import platform
|
|||||||
from PyQt5 import QtWidgets, QtCore, QtGui
|
from PyQt5 import QtWidgets, QtCore, QtGui
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCCheckBox, FCSpinner, RadioSet, FCEntry
|
from AppGUI.GUIElements import FCCheckBox, FCSpinner, RadioSet, FCEntry
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -36,22 +36,31 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI):
|
|||||||
grid1 = QtWidgets.QGridLayout()
|
grid1 = QtWidgets.QGridLayout()
|
||||||
self.layout.addLayout(grid1)
|
self.layout.addLayout(grid1)
|
||||||
|
|
||||||
|
# Plot CB
|
||||||
self.plot_cb = FCCheckBox(label=_('Plot'))
|
self.plot_cb = FCCheckBox(label=_('Plot'))
|
||||||
self.plot_cb.setToolTip(
|
self.plot_cb.setToolTip(
|
||||||
"Plot (show) this object."
|
"Plot (show) this object."
|
||||||
)
|
)
|
||||||
grid1.addWidget(self.plot_cb, 0, 0)
|
grid1.addWidget(self.plot_cb, 0, 0)
|
||||||
|
|
||||||
|
# Solid CB
|
||||||
self.solid_cb = FCCheckBox(label=_('Solid'))
|
self.solid_cb = FCCheckBox(label=_('Solid'))
|
||||||
self.solid_cb.setToolTip(
|
self.solid_cb.setToolTip(
|
||||||
"Plot as solid circles."
|
"Plot as solid circles."
|
||||||
)
|
)
|
||||||
grid1.addWidget(self.solid_cb, 0, 1)
|
grid1.addWidget(self.solid_cb, 0, 1)
|
||||||
|
|
||||||
|
# Multicolored CB
|
||||||
|
self.multicolored_cb = FCCheckBox(label='%s' % _('M-Color'))
|
||||||
|
self.multicolored_cb.setToolTip(
|
||||||
|
_("Draw polygons in different colors.")
|
||||||
|
)
|
||||||
|
grid1.addWidget(self.multicolored_cb, 0, 2)
|
||||||
|
|
||||||
separator_line = QtWidgets.QFrame()
|
separator_line = QtWidgets.QFrame()
|
||||||
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||||
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||||
grid1.addWidget(separator_line, 1, 0, 1, 2)
|
grid1.addWidget(separator_line, 1, 0, 1, 3)
|
||||||
|
|
||||||
grid2 = QtWidgets.QGridLayout()
|
grid2 = QtWidgets.QGridLayout()
|
||||||
self.layout.addLayout(grid2)
|
self.layout.addLayout(grid2)
|
||||||
@@ -341,6 +350,12 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI):
|
|||||||
|
|
||||||
# Load the defaults values into the Excellon Format and Excellon Zeros fields
|
# Load the defaults values into the Excellon Format and Excellon Zeros fields
|
||||||
self.excellon_defaults_button.clicked.connect(self.on_excellon_defaults_button)
|
self.excellon_defaults_button.clicked.connect(self.on_excellon_defaults_button)
|
||||||
|
# Make sure that when the Excellon loading parameters are changed, the change is reflected in the
|
||||||
|
# Export Excellon parameters.
|
||||||
|
self.update_excellon_cb.stateChanged.connect(self.on_update_exc_export)
|
||||||
|
|
||||||
|
# call it once to make sure it is updated at startup
|
||||||
|
self.on_update_exc_export(state=self.app.defaults["excellon_update"])
|
||||||
|
|
||||||
def optimization_selection(self):
|
def optimization_selection(self):
|
||||||
if self.excellon_optimization_radio.get_value() == 'M':
|
if self.excellon_optimization_radio.get_value() == 'M':
|
||||||
@@ -413,3 +428,105 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI):
|
|||||||
self.app.preferencesUiManager.defaults_form_fields["excellon_format_upper_mm"].set_value('3')
|
self.app.preferencesUiManager.defaults_form_fields["excellon_format_upper_mm"].set_value('3')
|
||||||
self.app.preferencesUiManager.defaults_form_fields["excellon_zeros"].set_value('L')
|
self.app.preferencesUiManager.defaults_form_fields["excellon_zeros"].set_value('L')
|
||||||
self.app.preferencesUiManager.defaults_form_fields["excellon_units"].set_value('INCH')
|
self.app.preferencesUiManager.defaults_form_fields["excellon_units"].set_value('INCH')
|
||||||
|
|
||||||
|
def on_update_exc_export(self, state):
|
||||||
|
"""
|
||||||
|
This is handling the update of Excellon Export parameters based on the ones in the Excellon General but only
|
||||||
|
if the update_excellon_cb checkbox is checked
|
||||||
|
|
||||||
|
:param state: state of the checkbox whose signals is tied to his slot
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if state:
|
||||||
|
# first try to disconnect
|
||||||
|
try:
|
||||||
|
self.excellon_format_upper_in_entry.returnPressed.disconnect(self.on_excellon_format_changed)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
self.excellon_format_lower_in_entry.returnPressed.disconnect(self.on_excellon_format_changed)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
self.excellon_format_upper_mm_entry.returnPressed.disconnect(self.on_excellon_format_changed)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
self.excellon_format_lower_mm_entry.returnPressed.disconnect(self.on_excellon_format_changed)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.excellon_zeros_radio.activated_custom.disconnect(self.on_excellon_zeros_changed)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
self.excellon_units_radio.activated_custom.disconnect(self.on_excellon_zeros_changed)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# the connect them
|
||||||
|
self.excellon_format_upper_in_entry.returnPressed.connect(self.on_excellon_format_changed)
|
||||||
|
self.excellon_format_lower_in_entry.returnPressed.connect(self.on_excellon_format_changed)
|
||||||
|
self.excellon_format_upper_mm_entry.returnPressed.connect(self.on_excellon_format_changed)
|
||||||
|
self.excellon_format_lower_mm_entry.returnPressed.connect(self.on_excellon_format_changed)
|
||||||
|
self.excellon_zeros_radio.activated_custom.connect(self.on_excellon_zeros_changed)
|
||||||
|
self.excellon_units_radio.activated_custom.connect(self.on_excellon_units_changed)
|
||||||
|
else:
|
||||||
|
# disconnect the signals
|
||||||
|
try:
|
||||||
|
self.excellon_format_upper_in_entry.returnPressed.disconnect(self.on_excellon_format_changed)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
self.excellon_format_lower_in_entry.returnPressed.disconnect(self.on_excellon_format_changed)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
self.excellon_format_upper_mm_entry.returnPressed.disconnect(self.on_excellon_format_changed)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
self.excellon_format_lower_mm_entry.returnPressed.disconnect(self.on_excellon_format_changed)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.excellon_zeros_radio.activated_custom.disconnect(self.on_excellon_zeros_changed)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
self.excellon_units_radio.activated_custom.disconnect(self.on_excellon_zeros_changed)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_excellon_format_changed(self):
|
||||||
|
"""
|
||||||
|
Slot activated when the user changes the Excellon format values in Preferences -> Excellon -> Excellon General
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
if self.excellon_units_radio.get_value().upper() == 'METRIC':
|
||||||
|
self.app.ui.excellon_defaults_form.excellon_exp_group.format_whole_entry.set_value(
|
||||||
|
self.excellon_format_upper_mm_entry.get_value())
|
||||||
|
self.app.ui.excellon_defaults_form.excellon_exp_group.format_dec_entry.set_value(
|
||||||
|
self.excellon_format_lower_mm_entry.get_value())
|
||||||
|
else:
|
||||||
|
self.app.ui.excellon_defaults_form.excellon_exp_group.format_whole_entry.set_value(
|
||||||
|
self.excellon_format_upper_in_entry.get_value())
|
||||||
|
self.app.ui.excellon_defaults_form.excellon_exp_group.format_dec_entry.set_value(
|
||||||
|
self.excellon_format_lower_in_entry.get_value())
|
||||||
|
|
||||||
|
def on_excellon_zeros_changed(self, val):
|
||||||
|
"""
|
||||||
|
Slot activated when the user changes the Excellon zeros values in Preferences -> Excellon -> Excellon General
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
self.app.ui.excellon_defaults_form.excellon_exp_group.zeros_radio.set_value(val + 'Z')
|
||||||
|
|
||||||
|
def on_excellon_units_changed(self, val):
|
||||||
|
"""
|
||||||
|
Slot activated when the user changes the Excellon unit values in Preferences -> Excellon -> Excellon General
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
self.app.ui.excellon_defaults_form.excellon_exp_group.excellon_units_radio.set_value(val)
|
||||||
|
self.on_excellon_format_changed()
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import Qt, QSettings
|
from PyQt5.QtCore import Qt, QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, FCEntry, FCSpinner, OptionalInputSection, \
|
from AppGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, FCEntry, FCSpinner, OptionalInputSection, \
|
||||||
FCComboBox
|
FCComboBox, NumericalEvalTupleEntry
|
||||||
from flatcamGUI.preferences import machinist_setting
|
from AppGUI.preferences import machinist_setting
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -198,7 +198,7 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI):
|
|||||||
"If no value is entered then there is no move\n"
|
"If no value is entered then there is no move\n"
|
||||||
"on X,Y plane at the end of the job.")
|
"on X,Y plane at the end of the job.")
|
||||||
)
|
)
|
||||||
self.endxy_entry = FCEntry()
|
self.endxy_entry = NumericalEvalTupleEntry(border_color='#0069A9')
|
||||||
|
|
||||||
grid2.addWidget(endmove_xy_label, 9, 0)
|
grid2.addWidget(endmove_xy_label, 9, 0)
|
||||||
grid2.addWidget(self.endxy_entry, 9, 1)
|
grid2.addWidget(self.endxy_entry, 9, 1)
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.preferences.excellon.ExcellonEditorPrefGroupUI import ExcellonEditorPrefGroupUI
|
from AppGUI.preferences.excellon.ExcellonEditorPrefGroupUI import ExcellonEditorPrefGroupUI
|
||||||
from flatcamGUI.preferences.excellon.ExcellonExpPrefGroupUI import ExcellonExpPrefGroupUI
|
from AppGUI.preferences.excellon.ExcellonExpPrefGroupUI import ExcellonExpPrefGroupUI
|
||||||
from flatcamGUI.preferences.excellon.ExcellonAdvOptPrefGroupUI import ExcellonAdvOptPrefGroupUI
|
from AppGUI.preferences.excellon.ExcellonAdvOptPrefGroupUI import ExcellonAdvOptPrefGroupUI
|
||||||
from flatcamGUI.preferences.excellon.ExcellonOptPrefGroupUI import ExcellonOptPrefGroupUI
|
from AppGUI.preferences.excellon.ExcellonOptPrefGroupUI import ExcellonOptPrefGroupUI
|
||||||
from flatcamGUI.preferences.excellon.ExcellonGenPrefGroupUI import ExcellonGenPrefGroupUI
|
from AppGUI.preferences.excellon.ExcellonGenPrefGroupUI import ExcellonGenPrefGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -31,7 +31,7 @@ class ExcellonPreferencesUI(QtWidgets.QWidget):
|
|||||||
self.decimals = decimals
|
self.decimals = decimals
|
||||||
|
|
||||||
self.excellon_gen_group = ExcellonGenPrefGroupUI(decimals=self.decimals)
|
self.excellon_gen_group = ExcellonGenPrefGroupUI(decimals=self.decimals)
|
||||||
self.excellon_gen_group.setMinimumWidth(220)
|
self.excellon_gen_group.setMinimumWidth(240)
|
||||||
self.excellon_opt_group = ExcellonOptPrefGroupUI(decimals=self.decimals)
|
self.excellon_opt_group = ExcellonOptPrefGroupUI(decimals=self.decimals)
|
||||||
self.excellon_opt_group.setMinimumWidth(290)
|
self.excellon_opt_group.setMinimumWidth(290)
|
||||||
self.excellon_exp_group = ExcellonExpPrefGroupUI(decimals=self.decimals)
|
self.excellon_exp_group = ExcellonExpPrefGroupUI(decimals=self.decimals)
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
from PyQt5 import QtCore, QtWidgets, QtGui
|
from PyQt5 import QtCore, QtWidgets, QtGui
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCDoubleSpinner, FCCheckBox, FCComboBox, RadioSet, OptionalInputSection, FCSpinner, \
|
from AppGUI.GUIElements import FCDoubleSpinner, FCCheckBox, FCComboBox, RadioSet, OptionalInputSection, FCSpinner, \
|
||||||
FCEntry
|
FCEntry
|
||||||
from flatcamGUI.preferences import settings
|
from AppGUI.preferences import settings
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -177,14 +177,6 @@ class GeneralAPPSetGroupUI(OptionsGroupUI):
|
|||||||
{'label': _('Landscape'), 'value': 'l'},
|
{'label': _('Landscape'), 'value': 'l'},
|
||||||
], stretch=False)
|
], stretch=False)
|
||||||
|
|
||||||
self.wks = OptionalInputSection(self.workspace_cb,
|
|
||||||
[
|
|
||||||
self.workspace_type_lbl,
|
|
||||||
self.wk_cb,
|
|
||||||
self.wk_orientation_label,
|
|
||||||
self.wk_orientation_radio
|
|
||||||
])
|
|
||||||
|
|
||||||
grid0.addWidget(self.wk_orientation_label, 8, 0)
|
grid0.addWidget(self.wk_orientation_label, 8, 0)
|
||||||
grid0.addWidget(self.wk_orientation_radio, 8, 1)
|
grid0.addWidget(self.wk_orientation_radio, 8, 1)
|
||||||
|
|
||||||
@@ -201,7 +193,7 @@ class GeneralAPPSetGroupUI(OptionsGroupUI):
|
|||||||
self.notebook_font_size_label = QtWidgets.QLabel('%s:' % _('Notebook'))
|
self.notebook_font_size_label = QtWidgets.QLabel('%s:' % _('Notebook'))
|
||||||
self.notebook_font_size_label.setToolTip(
|
self.notebook_font_size_label.setToolTip(
|
||||||
_("This sets the font size for the elements found in the Notebook.\n"
|
_("This sets the font size for the elements found in the Notebook.\n"
|
||||||
"The notebook is the collapsible area in the left side of the GUI,\n"
|
"The notebook is the collapsible area in the left side of the AppGUI,\n"
|
||||||
"and include the Project, Selected and Tool tabs.")
|
"and include the Project, Selected and Tool tabs.")
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -240,7 +232,7 @@ class GeneralAPPSetGroupUI(OptionsGroupUI):
|
|||||||
# TextBox Font Size
|
# TextBox Font Size
|
||||||
self.textbox_font_size_label = QtWidgets.QLabel('%s:' % _('Textbox'))
|
self.textbox_font_size_label = QtWidgets.QLabel('%s:' % _('Textbox'))
|
||||||
self.textbox_font_size_label.setToolTip(
|
self.textbox_font_size_label.setToolTip(
|
||||||
_("This sets the font size for the Textbox GUI\n"
|
_("This sets the font size for the Textbox AppGUI\n"
|
||||||
"elements that are used in FlatCAM.")
|
"elements that are used in FlatCAM.")
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -257,10 +249,29 @@ class GeneralAPPSetGroupUI(OptionsGroupUI):
|
|||||||
grid0.addWidget(self.textbox_font_size_label, 13, 0)
|
grid0.addWidget(self.textbox_font_size_label, 13, 0)
|
||||||
grid0.addWidget(self.textbox_font_size_spinner, 13, 1)
|
grid0.addWidget(self.textbox_font_size_spinner, 13, 1)
|
||||||
|
|
||||||
|
# HUD Font Size
|
||||||
|
self.hud_font_size_label = QtWidgets.QLabel('%s:' % _('HUD'))
|
||||||
|
self.hud_font_size_label.setToolTip(
|
||||||
|
_("This sets the font size for the Heads Up Display.")
|
||||||
|
)
|
||||||
|
|
||||||
|
self.hud_font_size_spinner = FCSpinner()
|
||||||
|
self.hud_font_size_spinner.set_range(8, 40)
|
||||||
|
self.hud_font_size_spinner.setWrapping(True)
|
||||||
|
|
||||||
|
qsettings = QSettings("Open Source", "FlatCAM")
|
||||||
|
if qsettings.contains("hud_font_size"):
|
||||||
|
self.hud_font_size_spinner.set_value(settings.value('hud_font_size', type=int))
|
||||||
|
else:
|
||||||
|
self.hud_font_size_spinner.set_value(8)
|
||||||
|
|
||||||
|
grid0.addWidget(self.hud_font_size_label, 14, 0)
|
||||||
|
grid0.addWidget(self.hud_font_size_spinner, 14, 1)
|
||||||
|
|
||||||
separator_line = QtWidgets.QFrame()
|
separator_line = QtWidgets.QFrame()
|
||||||
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||||
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||||
grid0.addWidget(separator_line, 14, 0, 1, 2)
|
grid0.addWidget(separator_line, 16, 0, 1, 2)
|
||||||
|
|
||||||
# -----------------------------------------------------------
|
# -----------------------------------------------------------
|
||||||
# -------------- MOUSE SETTINGS -----------------------------
|
# -------------- MOUSE SETTINGS -----------------------------
|
||||||
@@ -3,12 +3,12 @@ import sys
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import RadioSet, FCSpinner, FCCheckBox, FCComboBox, FCButton, OptionalInputSection, \
|
from AppGUI.GUIElements import RadioSet, FCSpinner, FCCheckBox, FCComboBox, FCButton, OptionalInputSection, \
|
||||||
FCDoubleSpinner
|
FCDoubleSpinner
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -380,12 +380,11 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
|
|||||||
|
|
||||||
def on_toggle_shell_from_settings(self, state):
|
def on_toggle_shell_from_settings(self, state):
|
||||||
"""
|
"""
|
||||||
Toggle shell: if is visible close it, if it is closed then open it
|
Toggle shell ui: if is visible close it, if it is closed then open it
|
||||||
|
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.app.defaults.report_usage("on_toggle_shell_from_settings()")
|
|
||||||
|
|
||||||
if state is True:
|
if state is True:
|
||||||
if not self.app.ui.shell_dock.isVisible():
|
if not self.app.ui.shell_dock.isVisible():
|
||||||
self.app.ui.shell_dock.show()
|
self.app.ui.shell_dock.show()
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets, QtCore, QtGui
|
from PyQt5 import QtWidgets, QtCore, QtGui
|
||||||
from PyQt5.QtCore import QSettings, Qt
|
from PyQt5.QtCore import QSettings, Qt
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import RadioSet, FCCheckBox, FCButton, FCComboBox, FCEntry, FCSpinner
|
from AppGUI.GUIElements import RadioSet, FCCheckBox, FCButton, FCComboBox, FCEntry, FCSpinner
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -56,13 +56,13 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
|
|||||||
)
|
)
|
||||||
grid0.addWidget(self.gray_icons_cb, 1, 0, 1, 3)
|
grid0.addWidget(self.gray_icons_cb, 1, 0, 1, 3)
|
||||||
|
|
||||||
self.theme_button = FCButton(_("Apply Theme"))
|
# self.theme_button = FCButton(_("Apply Theme"))
|
||||||
self.theme_button.setToolTip(
|
# self.theme_button.setToolTip(
|
||||||
_("Select a theme for FlatCAM.\n"
|
# _("Select a theme for FlatCAM.\n"
|
||||||
"It will theme the plot area.\n"
|
# "It will theme the plot area.\n"
|
||||||
"The application will restart after change.")
|
# "The application will restart after change.")
|
||||||
)
|
# )
|
||||||
grid0.addWidget(self.theme_button, 2, 0, 1, 3)
|
# grid0.addWidget(self.theme_button, 2, 0, 1, 3)
|
||||||
|
|
||||||
separator_line = QtWidgets.QFrame()
|
separator_line = QtWidgets.QFrame()
|
||||||
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||||
@@ -381,8 +381,6 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
|
|||||||
|
|
||||||
self.layout.addStretch()
|
self.layout.addStretch()
|
||||||
|
|
||||||
self.theme_button.clicked.connect(self.on_theme_change)
|
|
||||||
|
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
# ############################# GUI COLORS SIGNALS ############################
|
# ############################# GUI COLORS SIGNALS ############################
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
@@ -418,16 +416,6 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
|
|||||||
|
|
||||||
self.layout_combo.activated.connect(self.on_layout)
|
self.layout_combo.activated.connect(self.on_layout)
|
||||||
|
|
||||||
def on_theme_change(self):
|
|
||||||
val = self.theme_radio.get_value()
|
|
||||||
qsettings = QSettings("Open Source", "FlatCAM")
|
|
||||||
qsettings.setValue('theme', val)
|
|
||||||
|
|
||||||
# This will write the setting to the platform specific storage.
|
|
||||||
del qsettings
|
|
||||||
|
|
||||||
self.app.on_app_restart()
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def handle_style(style):
|
def handle_style(style):
|
||||||
# set current style
|
# set current style
|
||||||
@@ -659,14 +647,13 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
|
|||||||
# first remove the toolbars:
|
# first remove the toolbars:
|
||||||
try:
|
try:
|
||||||
self.app.ui.removeToolBar(self.app.ui.toolbarfile)
|
self.app.ui.removeToolBar(self.app.ui.toolbarfile)
|
||||||
self.app.ui.removeToolBar(self.app.ui.toolbargeo)
|
self.app.ui.removeToolBar(self.app.ui.toolbaredit)
|
||||||
self.app.ui.removeToolBar(self.app.ui.toolbarview)
|
self.app.ui.removeToolBar(self.app.ui.toolbarview)
|
||||||
self.app.ui.removeToolBar(self.app.ui.toolbarshell)
|
self.app.ui.removeToolBar(self.app.ui.toolbarshell)
|
||||||
self.app.ui.removeToolBar(self.app.ui.toolbartools)
|
self.app.ui.removeToolBar(self.app.ui.toolbartools)
|
||||||
self.app.ui.removeToolBar(self.app.ui.exc_edit_toolbar)
|
self.app.ui.removeToolBar(self.app.ui.exc_edit_toolbar)
|
||||||
self.app.ui.removeToolBar(self.app.ui.geo_edit_toolbar)
|
self.app.ui.removeToolBar(self.app.ui.geo_edit_toolbar)
|
||||||
self.app.ui.removeToolBar(self.app.ui.grb_edit_toolbar)
|
self.app.ui.removeToolBar(self.app.ui.grb_edit_toolbar)
|
||||||
self.app.ui.removeToolBar(self.app.ui.snap_toolbar)
|
|
||||||
self.app.ui.removeToolBar(self.app.ui.toolbarshell)
|
self.app.ui.removeToolBar(self.app.ui.toolbarshell)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
@@ -677,9 +664,9 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
|
|||||||
self.app.ui.toolbarfile.setObjectName('File_TB')
|
self.app.ui.toolbarfile.setObjectName('File_TB')
|
||||||
self.app.ui.addToolBar(Qt.LeftToolBarArea, self.app.ui.toolbarfile)
|
self.app.ui.addToolBar(Qt.LeftToolBarArea, self.app.ui.toolbarfile)
|
||||||
|
|
||||||
self.app.ui.toolbargeo = QtWidgets.QToolBar('Edit Toolbar')
|
self.app.ui.toolbaredit = QtWidgets.QToolBar('Edit Toolbar')
|
||||||
self.app.ui.toolbargeo.setObjectName('Edit_TB')
|
self.app.ui.toolbaredit.setObjectName('Edit_TB')
|
||||||
self.app.ui.addToolBar(Qt.LeftToolBarArea, self.app.ui.toolbargeo)
|
self.app.ui.addToolBar(Qt.LeftToolBarArea, self.app.ui.toolbaredit)
|
||||||
|
|
||||||
self.app.ui.toolbarshell = QtWidgets.QToolBar('Shell Toolbar')
|
self.app.ui.toolbarshell = QtWidgets.QToolBar('Shell Toolbar')
|
||||||
self.app.ui.toolbarshell.setObjectName('Shell_TB')
|
self.app.ui.toolbarshell.setObjectName('Shell_TB')
|
||||||
@@ -709,22 +696,15 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
|
|||||||
self.app.ui.exc_edit_toolbar.setObjectName('ExcEditor_TB')
|
self.app.ui.exc_edit_toolbar.setObjectName('ExcEditor_TB')
|
||||||
self.app.ui.addToolBar(Qt.RightToolBarArea, self.app.ui.exc_edit_toolbar)
|
self.app.ui.addToolBar(Qt.RightToolBarArea, self.app.ui.exc_edit_toolbar)
|
||||||
|
|
||||||
self.app.ui.snap_toolbar = QtWidgets.QToolBar('Grid Toolbar')
|
|
||||||
self.app.ui.snap_toolbar.setObjectName('Snap_TB')
|
|
||||||
self.app.ui.snap_toolbar.setMaximumHeight(30)
|
|
||||||
self.app.ui.splitter_left.addWidget(self.app.ui.snap_toolbar)
|
|
||||||
|
|
||||||
self.app.ui.corner_snap_btn.setVisible(True)
|
|
||||||
self.app.ui.snap_magnet.setVisible(True)
|
|
||||||
else:
|
else:
|
||||||
# ## TOOLBAR INSTALLATION # ##
|
# ## TOOLBAR INSTALLATION # ##
|
||||||
self.app.ui.toolbarfile = QtWidgets.QToolBar('File Toolbar')
|
self.app.ui.toolbarfile = QtWidgets.QToolBar('File Toolbar')
|
||||||
self.app.ui.toolbarfile.setObjectName('File_TB')
|
self.app.ui.toolbarfile.setObjectName('File_TB')
|
||||||
self.app.ui.addToolBar(self.app.ui.toolbarfile)
|
self.app.ui.addToolBar(self.app.ui.toolbarfile)
|
||||||
|
|
||||||
self.app.ui.toolbargeo = QtWidgets.QToolBar('Edit Toolbar')
|
self.app.ui.toolbaredit = QtWidgets.QToolBar('Edit Toolbar')
|
||||||
self.app.ui.toolbargeo.setObjectName('Edit_TB')
|
self.app.ui.toolbaredit.setObjectName('Edit_TB')
|
||||||
self.app.ui.addToolBar(self.app.ui.toolbargeo)
|
self.app.ui.addToolBar(self.app.ui.toolbaredit)
|
||||||
|
|
||||||
self.app.ui.toolbarview = QtWidgets.QToolBar('View Toolbar')
|
self.app.ui.toolbarview = QtWidgets.QToolBar('View Toolbar')
|
||||||
self.app.ui.toolbarview.setObjectName('View_TB')
|
self.app.ui.toolbarview.setObjectName('View_TB')
|
||||||
@@ -755,18 +735,9 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
|
|||||||
self.app.ui.grb_edit_toolbar.setObjectName('GrbEditor_TB')
|
self.app.ui.grb_edit_toolbar.setObjectName('GrbEditor_TB')
|
||||||
self.app.ui.addToolBar(self.app.ui.grb_edit_toolbar)
|
self.app.ui.addToolBar(self.app.ui.grb_edit_toolbar)
|
||||||
|
|
||||||
self.app.ui.snap_toolbar = QtWidgets.QToolBar('Grid Toolbar')
|
|
||||||
self.app.ui.snap_toolbar.setObjectName('Snap_TB')
|
|
||||||
# self.app.ui.snap_toolbar.setMaximumHeight(30)
|
|
||||||
self.app.ui.addToolBar(self.app.ui.snap_toolbar)
|
|
||||||
|
|
||||||
self.app.ui.corner_snap_btn.setVisible(False)
|
|
||||||
self.app.ui.snap_magnet.setVisible(False)
|
|
||||||
|
|
||||||
if current_layout == 'minimal':
|
if current_layout == 'minimal':
|
||||||
self.app.ui.toolbarview.setVisible(False)
|
self.app.ui.toolbarview.setVisible(False)
|
||||||
self.app.ui.toolbarshell.setVisible(False)
|
self.app.ui.toolbarshell.setVisible(False)
|
||||||
self.app.ui.snap_toolbar.setVisible(False)
|
|
||||||
self.app.ui.geo_edit_toolbar.setVisible(False)
|
self.app.ui.geo_edit_toolbar.setVisible(False)
|
||||||
self.app.ui.grb_edit_toolbar.setVisible(False)
|
self.app.ui.grb_edit_toolbar.setVisible(False)
|
||||||
self.app.ui.exc_edit_toolbar.setVisible(False)
|
self.app.ui.exc_edit_toolbar.setVisible(False)
|
||||||
@@ -779,7 +750,9 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
|
|||||||
self.app.connect_toolbar_signals()
|
self.app.connect_toolbar_signals()
|
||||||
|
|
||||||
self.app.ui.grid_snap_btn.setChecked(True)
|
self.app.ui.grid_snap_btn.setChecked(True)
|
||||||
self.app.ui.on_grid_snap_triggered(state=True)
|
|
||||||
|
self.app.ui.corner_snap_btn.setVisible(False)
|
||||||
|
self.app.ui.snap_magnet.setVisible(False)
|
||||||
|
|
||||||
self.app.ui.grid_gap_x_entry.setText(str(self.app.defaults["global_gridx"]))
|
self.app.ui.grid_gap_x_entry.setText(str(self.app.defaults["global_gridx"]))
|
||||||
self.app.ui.grid_gap_y_entry.setText(str(self.app.defaults["global_gridy"]))
|
self.app.ui.grid_gap_y_entry.setText(str(self.app.defaults["global_gridy"]))
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.preferences.general.GeneralAppPrefGroupUI import GeneralAppPrefGroupUI
|
from AppGUI.preferences.general.GeneralAppPrefGroupUI import GeneralAppPrefGroupUI
|
||||||
from flatcamGUI.preferences.general.GeneralAPPSetGroupUI import GeneralAPPSetGroupUI
|
from AppGUI.preferences.general.GeneralAPPSetGroupUI import GeneralAPPSetGroupUI
|
||||||
from flatcamGUI.preferences.general.GeneralGUIPrefGroupUI import GeneralGUIPrefGroupUI
|
from AppGUI.preferences.general.GeneralGUIPrefGroupUI import GeneralGUIPrefGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCEntry, FloatEntry, FCDoubleSpinner, FCCheckBox, RadioSet, FCLabel
|
from AppGUI.GUIElements import FCDoubleSpinner, FCCheckBox, RadioSet, FCLabel, NumericalEvalTupleEntry, \
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
NumericalEvalEntry
|
||||||
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -46,8 +47,9 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
|
|||||||
toolchange_xy_label.setToolTip(
|
toolchange_xy_label.setToolTip(
|
||||||
_("Toolchange X,Y position.")
|
_("Toolchange X,Y position.")
|
||||||
)
|
)
|
||||||
|
self.toolchangexy_entry = NumericalEvalTupleEntry(border_color='#0069A9')
|
||||||
|
|
||||||
grid1.addWidget(toolchange_xy_label, 1, 0)
|
grid1.addWidget(toolchange_xy_label, 1, 0)
|
||||||
self.toolchangexy_entry = FCEntry()
|
|
||||||
grid1.addWidget(self.toolchangexy_entry, 1, 1)
|
grid1.addWidget(self.toolchangexy_entry, 1, 1)
|
||||||
|
|
||||||
# Start move Z
|
# Start move Z
|
||||||
@@ -56,8 +58,9 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
|
|||||||
_("Height of the tool just after starting the work.\n"
|
_("Height of the tool just after starting the work.\n"
|
||||||
"Delete the value if you don't need this feature.")
|
"Delete the value if you don't need this feature.")
|
||||||
)
|
)
|
||||||
|
self.gstartz_entry = NumericalEvalEntry(border_color='#0069A9')
|
||||||
|
|
||||||
grid1.addWidget(startzlabel, 2, 0)
|
grid1.addWidget(startzlabel, 2, 0)
|
||||||
self.gstartz_entry = FloatEntry()
|
|
||||||
grid1.addWidget(self.gstartz_entry, 2, 1)
|
grid1.addWidget(self.gstartz_entry, 2, 1)
|
||||||
|
|
||||||
# Feedrate rapids
|
# Feedrate rapids
|
||||||
@@ -186,6 +189,11 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
|
|||||||
grid1.addWidget(segy_label, 11, 0)
|
grid1.addWidget(segy_label, 11, 0)
|
||||||
grid1.addWidget(self.segy_entry, 11, 1)
|
grid1.addWidget(self.segy_entry, 11, 1)
|
||||||
|
|
||||||
|
separator_line = QtWidgets.QFrame()
|
||||||
|
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||||
|
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||||
|
grid1.addWidget(separator_line, 12, 0, 1, 2)
|
||||||
|
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
# --- Area Exclusion ----------
|
# --- Area Exclusion ----------
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
@@ -195,10 +203,10 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
|
|||||||
"Those parameters are available only for\n"
|
"Those parameters are available only for\n"
|
||||||
"Advanced App. Level.")
|
"Advanced App. Level.")
|
||||||
)
|
)
|
||||||
grid1.addWidget(self.adv_label, 12, 0, 1, 2)
|
grid1.addWidget(self.adv_label, 13, 0, 1, 2)
|
||||||
|
|
||||||
# Exclusion Area CB
|
# Exclusion Area CB
|
||||||
self.exclusion_cb = FCCheckBox('%s:' % _("Exclusion areas"))
|
self.exclusion_cb = FCCheckBox('%s' % _("Exclusion areas"))
|
||||||
self.exclusion_cb.setToolTip(
|
self.exclusion_cb.setToolTip(
|
||||||
_(
|
_(
|
||||||
"Include exclusion areas.\n"
|
"Include exclusion areas.\n"
|
||||||
@@ -206,7 +214,7 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
|
|||||||
"is forbidden."
|
"is forbidden."
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
grid1.addWidget(self.exclusion_cb, 13, 0, 1, 2)
|
grid1.addWidget(self.exclusion_cb, 14, 0, 1, 2)
|
||||||
|
|
||||||
# Area Selection shape
|
# Area Selection shape
|
||||||
self.area_shape_label = QtWidgets.QLabel('%s:' % _("Shape"))
|
self.area_shape_label = QtWidgets.QLabel('%s:' % _("Shape"))
|
||||||
@@ -217,8 +225,8 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
|
|||||||
self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'},
|
self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'},
|
||||||
{'label': _("Polygon"), 'value': 'polygon'}])
|
{'label': _("Polygon"), 'value': 'polygon'}])
|
||||||
|
|
||||||
grid1.addWidget(self.area_shape_label, 14, 0)
|
grid1.addWidget(self.area_shape_label, 15, 0)
|
||||||
grid1.addWidget(self.area_shape_radio, 14, 1)
|
grid1.addWidget(self.area_shape_radio, 15, 1)
|
||||||
|
|
||||||
# Chose Strategy
|
# Chose Strategy
|
||||||
self.strategy_label = FCLabel('%s:' % _("Strategy"))
|
self.strategy_label = FCLabel('%s:' % _("Strategy"))
|
||||||
@@ -229,8 +237,8 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
|
|||||||
self.strategy_radio = RadioSet([{'label': _('Over'), 'value': 'over'},
|
self.strategy_radio = RadioSet([{'label': _('Over'), 'value': 'over'},
|
||||||
{'label': _('Around'), 'value': 'around'}])
|
{'label': _('Around'), 'value': 'around'}])
|
||||||
|
|
||||||
grid1.addWidget(self.strategy_label, 15, 0)
|
grid1.addWidget(self.strategy_label, 16, 0)
|
||||||
grid1.addWidget(self.strategy_radio, 15, 1)
|
grid1.addWidget(self.strategy_radio, 16, 1)
|
||||||
|
|
||||||
# Over Z
|
# Over Z
|
||||||
self.over_z_label = FCLabel('%s:' % _("Over Z"))
|
self.over_z_label = FCLabel('%s:' % _("Over Z"))
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCSpinner, RadioSet
|
from AppGUI.GUIElements import FCSpinner, RadioSet
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets, QtCore, QtGui
|
from PyQt5 import QtWidgets, QtCore, QtGui
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCCheckBox, FCSpinner, FCEntry
|
from AppGUI.GUIElements import FCCheckBox, FCSpinner, FCEntry
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -31,12 +31,22 @@ class GeometryGenPrefGroupUI(OptionsGroupUI):
|
|||||||
self.plot_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("Plot Options"))
|
self.plot_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("Plot Options"))
|
||||||
self.layout.addWidget(self.plot_options_label)
|
self.layout.addWidget(self.plot_options_label)
|
||||||
|
|
||||||
|
plot_hlay = QtWidgets.QHBoxLayout()
|
||||||
|
self.layout.addLayout((plot_hlay))
|
||||||
|
|
||||||
# Plot CB
|
# Plot CB
|
||||||
self.plot_cb = FCCheckBox(label=_('Plot'))
|
self.plot_cb = FCCheckBox(label=_('Plot'))
|
||||||
self.plot_cb.setToolTip(
|
self.plot_cb.setToolTip(
|
||||||
_("Plot (show) this object.")
|
_("Plot (show) this object.")
|
||||||
)
|
)
|
||||||
self.layout.addWidget(self.plot_cb)
|
plot_hlay.addWidget(self.plot_cb)
|
||||||
|
|
||||||
|
# Multicolored CB
|
||||||
|
self.multicolored_cb = FCCheckBox(label=_('M-Color'))
|
||||||
|
self.multicolored_cb.setToolTip(
|
||||||
|
_("Draw polygons in different colors.")
|
||||||
|
)
|
||||||
|
plot_hlay.addWidget(self.multicolored_cb)
|
||||||
|
|
||||||
grid0 = QtWidgets.QGridLayout()
|
grid0 = QtWidgets.QGridLayout()
|
||||||
self.layout.addLayout(grid0)
|
self.layout.addLayout(grid0)
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import Qt, QSettings
|
from PyQt5.QtCore import Qt, QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCDoubleSpinner, FCCheckBox, OptionalInputSection, FCEntry, FCSpinner, FCComboBox
|
from AppGUI.GUIElements import FCDoubleSpinner, FCCheckBox, OptionalInputSection, FCSpinner, FCComboBox, \
|
||||||
from flatcamGUI.preferences import machinist_setting
|
NumericalEvalTupleEntry
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences import machinist_setting
|
||||||
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -176,7 +177,7 @@ class GeometryOptPrefGroupUI(OptionsGroupUI):
|
|||||||
"If no value is entered then there is no move\n"
|
"If no value is entered then there is no move\n"
|
||||||
"on X,Y plane at the end of the job.")
|
"on X,Y plane at the end of the job.")
|
||||||
)
|
)
|
||||||
self.endxy_entry = FCEntry()
|
self.endxy_entry = NumericalEvalTupleEntry(border_color='#0069A9')
|
||||||
|
|
||||||
grid1.addWidget(endmove_xy_label, 7, 0)
|
grid1.addWidget(endmove_xy_label, 7, 0)
|
||||||
grid1.addWidget(self.endxy_entry, 7, 1)
|
grid1.addWidget(self.endxy_entry, 7, 1)
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.preferences.geometry.GeometryEditorPrefGroupUI import GeometryEditorPrefGroupUI
|
from AppGUI.preferences.geometry.GeometryEditorPrefGroupUI import GeometryEditorPrefGroupUI
|
||||||
from flatcamGUI.preferences.geometry.GeometryAdvOptPrefGroupUI import GeometryAdvOptPrefGroupUI
|
from AppGUI.preferences.geometry.GeometryAdvOptPrefGroupUI import GeometryAdvOptPrefGroupUI
|
||||||
from flatcamGUI.preferences.geometry.GeometryOptPrefGroupUI import GeometryOptPrefGroupUI
|
from AppGUI.preferences.geometry.GeometryOptPrefGroupUI import GeometryOptPrefGroupUI
|
||||||
from flatcamGUI.preferences.geometry.GeometryGenPrefGroupUI import GeometryGenPrefGroupUI
|
from AppGUI.preferences.geometry.GeometryGenPrefGroupUI import GeometryGenPrefGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCCheckBox, RadioSet, FCDoubleSpinner, FCSpinner, OptionalInputSection
|
from AppGUI.GUIElements import FCCheckBox, RadioSet, FCDoubleSpinner, FCSpinner, OptionalInputSection
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -63,85 +63,6 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI):
|
|||||||
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||||
grid0.addWidget(separator_line, 2, 0, 1, 2)
|
grid0.addWidget(separator_line, 2, 0, 1, 2)
|
||||||
|
|
||||||
# Tool Type
|
|
||||||
self.tool_type_label = QtWidgets.QLabel('<b>%s</b>' % _('Tool Type'))
|
|
||||||
self.tool_type_label.setToolTip(
|
|
||||||
_("Choose which tool to use for Gerber isolation:\n"
|
|
||||||
"'Circular' or 'V-shape'.\n"
|
|
||||||
"When the 'V-shape' is selected then the tool\n"
|
|
||||||
"diameter will depend on the chosen cut depth.")
|
|
||||||
)
|
|
||||||
self.tool_type_radio = RadioSet([{'label': 'Circular', 'value': 'circular'},
|
|
||||||
{'label': 'V-Shape', 'value': 'v'}])
|
|
||||||
|
|
||||||
grid0.addWidget(self.tool_type_label, 3, 0)
|
|
||||||
grid0.addWidget(self.tool_type_radio, 3, 1, 1, 2)
|
|
||||||
|
|
||||||
# Tip Dia
|
|
||||||
self.tipdialabel = QtWidgets.QLabel('%s:' % _('V-Tip Dia'))
|
|
||||||
self.tipdialabel.setToolTip(
|
|
||||||
_("The tip diameter for V-Shape Tool")
|
|
||||||
)
|
|
||||||
self.tipdia_spinner = FCDoubleSpinner()
|
|
||||||
self.tipdia_spinner.set_precision(self.decimals)
|
|
||||||
self.tipdia_spinner.set_range(-99.9999, 99.9999)
|
|
||||||
self.tipdia_spinner.setSingleStep(0.1)
|
|
||||||
self.tipdia_spinner.setWrapping(True)
|
|
||||||
grid0.addWidget(self.tipdialabel, 4, 0)
|
|
||||||
grid0.addWidget(self.tipdia_spinner, 4, 1, 1, 2)
|
|
||||||
|
|
||||||
# Tip Angle
|
|
||||||
self.tipanglelabel = QtWidgets.QLabel('%s:' % _('V-Tip Angle'))
|
|
||||||
self.tipanglelabel.setToolTip(
|
|
||||||
_("The tip angle for V-Shape Tool.\n"
|
|
||||||
"In degree.")
|
|
||||||
)
|
|
||||||
self.tipangle_spinner = FCSpinner()
|
|
||||||
self.tipangle_spinner.set_range(1, 180)
|
|
||||||
self.tipangle_spinner.set_step(5)
|
|
||||||
self.tipangle_spinner.setWrapping(True)
|
|
||||||
grid0.addWidget(self.tipanglelabel, 5, 0)
|
|
||||||
grid0.addWidget(self.tipangle_spinner, 5, 1, 1, 2)
|
|
||||||
|
|
||||||
# Cut Z
|
|
||||||
self.cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
|
|
||||||
self.cutzlabel.setToolTip(
|
|
||||||
_("Cutting depth (negative)\n"
|
|
||||||
"below the copper surface.")
|
|
||||||
)
|
|
||||||
self.cutz_spinner = FCDoubleSpinner()
|
|
||||||
self.cutz_spinner.set_precision(self.decimals)
|
|
||||||
self.cutz_spinner.set_range(-99.9999, 0.0000)
|
|
||||||
self.cutz_spinner.setSingleStep(0.1)
|
|
||||||
self.cutz_spinner.setWrapping(True)
|
|
||||||
|
|
||||||
grid0.addWidget(self.cutzlabel, 6, 0)
|
|
||||||
grid0.addWidget(self.cutz_spinner, 6, 1, 1, 2)
|
|
||||||
|
|
||||||
# Isolation Type
|
|
||||||
self.iso_type_label = QtWidgets.QLabel('%s:' % _('Isolation Type'))
|
|
||||||
self.iso_type_label.setToolTip(
|
|
||||||
_("Choose how the isolation will be executed:\n"
|
|
||||||
"- 'Full' -> complete isolation of polygons\n"
|
|
||||||
"- 'Ext' -> will isolate only on the outside\n"
|
|
||||||
"- 'Int' -> will isolate only on the inside\n"
|
|
||||||
"'Exterior' isolation is almost always possible\n"
|
|
||||||
"(with the right tool) but 'Interior'\n"
|
|
||||||
"isolation can be done only when there is an opening\n"
|
|
||||||
"inside of the polygon (e.g polygon is a 'doughnut' shape).")
|
|
||||||
)
|
|
||||||
self.iso_type_radio = RadioSet([{'label': _('Full'), 'value': 'full'},
|
|
||||||
{'label': _('Exterior'), 'value': 'ext'},
|
|
||||||
{'label': _('Interior'), 'value': 'int'}])
|
|
||||||
|
|
||||||
grid0.addWidget(self.iso_type_label, 7, 0,)
|
|
||||||
grid0.addWidget(self.iso_type_radio, 7, 1, 1, 2)
|
|
||||||
|
|
||||||
separator_line = QtWidgets.QFrame()
|
|
||||||
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
|
||||||
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
|
||||||
grid0.addWidget(separator_line, 8, 0, 1, 2)
|
|
||||||
|
|
||||||
# Buffering Type
|
# Buffering Type
|
||||||
buffering_label = QtWidgets.QLabel('%s:' % _('Buffering'))
|
buffering_label = QtWidgets.QLabel('%s:' % _('Buffering'))
|
||||||
buffering_label.setToolTip(
|
buffering_label.setToolTip(
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCSpinner, FCDoubleSpinner, FCComboBox, FCEntry, RadioSet
|
from AppGUI.GUIElements import FCSpinner, FCDoubleSpinner, FCComboBox, FCEntry, RadioSet, NumericalEvalTupleEntry
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -109,8 +109,9 @@ class GerberEditorPrefGroupUI(OptionsGroupUI):
|
|||||||
"The value of the diameter has to use the dot decimals separator.\n"
|
"The value of the diameter has to use the dot decimals separator.\n"
|
||||||
"Valid values: 0.3, 1.0")
|
"Valid values: 0.3, 1.0")
|
||||||
)
|
)
|
||||||
|
self.adddim_entry = NumericalEvalTupleEntry(border_color='#0069A9')
|
||||||
|
|
||||||
grid0.addWidget(self.adddim_label, 5, 0)
|
grid0.addWidget(self.adddim_label, 5, 0)
|
||||||
self.adddim_entry = FCEntry()
|
|
||||||
grid0.addWidget(self.adddim_entry, 5, 1)
|
grid0.addWidget(self.adddim_entry, 5, 1)
|
||||||
|
|
||||||
self.grb_array_linear_label = QtWidgets.QLabel('<b>%s:</b>' % _('Linear Pad Array'))
|
self.grb_array_linear_label = QtWidgets.QLabel('<b>%s:</b>' % _('Linear Pad Array'))
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets, QtCore
|
from PyQt5 import QtWidgets, QtCore
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import RadioSet, FCSpinner
|
from AppGUI.GUIElements import RadioSet, FCSpinner
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets, QtCore, QtGui
|
from PyQt5 import QtWidgets, QtCore, QtGui
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCCheckBox, FCSpinner, RadioSet, FCEntry
|
from AppGUI.GUIElements import FCCheckBox, FCSpinner, RadioSet, FCEntry
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -34,26 +34,26 @@ class GerberGenPrefGroupUI(OptionsGroupUI):
|
|||||||
grid0 = QtWidgets.QGridLayout()
|
grid0 = QtWidgets.QGridLayout()
|
||||||
self.layout.addLayout(grid0)
|
self.layout.addLayout(grid0)
|
||||||
|
|
||||||
|
# Plot CB
|
||||||
|
self.plot_cb = FCCheckBox(label='%s' % _('Plot'))
|
||||||
|
self.plot_options_label.setToolTip(
|
||||||
|
_("Plot (show) this object.")
|
||||||
|
)
|
||||||
|
grid0.addWidget(self.plot_cb, 0, 0)
|
||||||
|
|
||||||
# Solid CB
|
# Solid CB
|
||||||
self.solid_cb = FCCheckBox(label='%s' % _('Solid'))
|
self.solid_cb = FCCheckBox(label='%s' % _('Solid'))
|
||||||
self.solid_cb.setToolTip(
|
self.solid_cb.setToolTip(
|
||||||
_("Solid color polygons.")
|
_("Solid color polygons.")
|
||||||
)
|
)
|
||||||
grid0.addWidget(self.solid_cb, 0, 0)
|
grid0.addWidget(self.solid_cb, 0, 1)
|
||||||
|
|
||||||
# Multicolored CB
|
# Multicolored CB
|
||||||
self.multicolored_cb = FCCheckBox(label='%s' % _('M-Color'))
|
self.multicolored_cb = FCCheckBox(label='%s' % _('M-Color'))
|
||||||
self.multicolored_cb.setToolTip(
|
self.multicolored_cb.setToolTip(
|
||||||
_("Draw polygons in different colors.")
|
_("Draw polygons in different colors.")
|
||||||
)
|
)
|
||||||
grid0.addWidget(self.multicolored_cb, 0, 1)
|
grid0.addWidget(self.multicolored_cb, 0, 2)
|
||||||
|
|
||||||
# Plot CB
|
|
||||||
self.plot_cb = FCCheckBox(label='%s' % _('Plot'))
|
|
||||||
self.plot_options_label.setToolTip(
|
|
||||||
_("Plot (show) this object.")
|
|
||||||
)
|
|
||||||
grid0.addWidget(self.plot_cb, 0, 2)
|
|
||||||
|
|
||||||
# Number of circle steps for circular aperture linear approximation
|
# Number of circle steps for circular aperture linear approximation
|
||||||
self.circle_steps_label = QtWidgets.QLabel('%s:' % _("Circle Steps"))
|
self.circle_steps_label = QtWidgets.QLabel('%s:' % _("Circle Steps"))
|
||||||
100
AppGUI/preferences/gerber/GerberOptPrefGroupUI.py
Normal file
100
AppGUI/preferences/gerber/GerberOptPrefGroupUI.py
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
from PyQt5 import QtWidgets
|
||||||
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
|
from AppGUI.GUIElements import FCDoubleSpinner, FCSpinner, RadioSet, FCCheckBox, FCComboBox
|
||||||
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
|
import gettext
|
||||||
|
import AppTranslation as fcTranslate
|
||||||
|
import builtins
|
||||||
|
|
||||||
|
fcTranslate.apply_language('strings')
|
||||||
|
if '_' not in builtins.__dict__:
|
||||||
|
_ = gettext.gettext
|
||||||
|
|
||||||
|
settings = QSettings("Open Source", "FlatCAM")
|
||||||
|
if settings.contains("machinist"):
|
||||||
|
machinist_setting = settings.value('machinist', type=int)
|
||||||
|
else:
|
||||||
|
machinist_setting = 0
|
||||||
|
|
||||||
|
|
||||||
|
class GerberOptPrefGroupUI(OptionsGroupUI):
|
||||||
|
def __init__(self, decimals=4, parent=None):
|
||||||
|
# OptionsGroupUI.__init__(self, "Gerber Options Preferences", parent=parent)
|
||||||
|
super(GerberOptPrefGroupUI, self).__init__(self, parent=parent)
|
||||||
|
|
||||||
|
self.decimals = decimals
|
||||||
|
|
||||||
|
self.setTitle(str(_("Gerber Options")))
|
||||||
|
|
||||||
|
# ## Clear non-copper regions
|
||||||
|
self.clearcopper_label = QtWidgets.QLabel("<b>%s:</b>" % _("Non-copper regions"))
|
||||||
|
self.clearcopper_label.setToolTip(
|
||||||
|
_("Create polygons covering the\n"
|
||||||
|
"areas without copper on the PCB.\n"
|
||||||
|
"Equivalent to the inverse of this\n"
|
||||||
|
"object. Can be used to remove all\n"
|
||||||
|
"copper from a specified region.")
|
||||||
|
)
|
||||||
|
self.layout.addWidget(self.clearcopper_label)
|
||||||
|
|
||||||
|
grid1 = QtWidgets.QGridLayout()
|
||||||
|
self.layout.addLayout(grid1)
|
||||||
|
|
||||||
|
# Margin
|
||||||
|
bmlabel = QtWidgets.QLabel('%s:' % _('Boundary Margin'))
|
||||||
|
bmlabel.setToolTip(
|
||||||
|
_("Specify the edge of the PCB\n"
|
||||||
|
"by drawing a box around all\n"
|
||||||
|
"objects with this minimum\n"
|
||||||
|
"distance.")
|
||||||
|
)
|
||||||
|
grid1.addWidget(bmlabel, 0, 0)
|
||||||
|
self.noncopper_margin_entry = FCDoubleSpinner()
|
||||||
|
self.noncopper_margin_entry.set_precision(self.decimals)
|
||||||
|
self.noncopper_margin_entry.setSingleStep(0.1)
|
||||||
|
self.noncopper_margin_entry.set_range(-9999, 9999)
|
||||||
|
grid1.addWidget(self.noncopper_margin_entry, 0, 1)
|
||||||
|
|
||||||
|
# Rounded corners
|
||||||
|
self.noncopper_rounded_cb = FCCheckBox(label=_("Rounded Geo"))
|
||||||
|
self.noncopper_rounded_cb.setToolTip(
|
||||||
|
_("Resulting geometry will have rounded corners.")
|
||||||
|
)
|
||||||
|
grid1.addWidget(self.noncopper_rounded_cb, 1, 0, 1, 2)
|
||||||
|
|
||||||
|
separator_line = QtWidgets.QFrame()
|
||||||
|
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||||
|
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||||
|
grid1.addWidget(separator_line, 2, 0, 1, 2)
|
||||||
|
|
||||||
|
# ## Bounding box
|
||||||
|
self.boundingbox_label = QtWidgets.QLabel('<b>%s:</b>' % _('Bounding Box'))
|
||||||
|
self.layout.addWidget(self.boundingbox_label)
|
||||||
|
|
||||||
|
grid2 = QtWidgets.QGridLayout()
|
||||||
|
self.layout.addLayout(grid2)
|
||||||
|
|
||||||
|
bbmargin = QtWidgets.QLabel('%s:' % _('Boundary Margin'))
|
||||||
|
bbmargin.setToolTip(
|
||||||
|
_("Distance of the edges of the box\n"
|
||||||
|
"to the nearest polygon.")
|
||||||
|
)
|
||||||
|
self.bbmargin_entry = FCDoubleSpinner()
|
||||||
|
self.bbmargin_entry.set_precision(self.decimals)
|
||||||
|
self.bbmargin_entry.setSingleStep(0.1)
|
||||||
|
self.bbmargin_entry.set_range(-9999, 9999)
|
||||||
|
|
||||||
|
grid2.addWidget(bbmargin, 0, 0)
|
||||||
|
grid2.addWidget(self.bbmargin_entry, 0, 1)
|
||||||
|
|
||||||
|
self.bbrounded_cb = FCCheckBox(label='%s' % _("Rounded Geo"))
|
||||||
|
self.bbrounded_cb.setToolTip(
|
||||||
|
_("If the bounding box is \n"
|
||||||
|
"to have rounded corners\n"
|
||||||
|
"their radius is equal to\n"
|
||||||
|
"the margin.")
|
||||||
|
)
|
||||||
|
grid2.addWidget(self.bbrounded_cb, 1, 0, 1, 2)
|
||||||
|
self.layout.addStretch()
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.preferences.gerber.GerberEditorPrefGroupUI import GerberEditorPrefGroupUI
|
from AppGUI.preferences.gerber.GerberEditorPrefGroupUI import GerberEditorPrefGroupUI
|
||||||
from flatcamGUI.preferences.gerber.GerberExpPrefGroupUI import GerberExpPrefGroupUI
|
from AppGUI.preferences.gerber.GerberExpPrefGroupUI import GerberExpPrefGroupUI
|
||||||
from flatcamGUI.preferences.gerber.GerberAdvOptPrefGroupUI import GerberAdvOptPrefGroupUI
|
from AppGUI.preferences.gerber.GerberAdvOptPrefGroupUI import GerberAdvOptPrefGroupUI
|
||||||
from flatcamGUI.preferences.gerber.GerberOptPrefGroupUI import GerberOptPrefGroupUI
|
from AppGUI.preferences.gerber.GerberOptPrefGroupUI import GerberOptPrefGroupUI
|
||||||
from flatcamGUI.preferences.gerber.GerberGenPrefGroupUI import GerberGenPrefGroupUI
|
from AppGUI.preferences.gerber.GerberGenPrefGroupUI import GerberGenPrefGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -44,6 +44,7 @@ class GerberPreferencesUI(QtWidgets.QWidget):
|
|||||||
self.vlay = QtWidgets.QVBoxLayout()
|
self.vlay = QtWidgets.QVBoxLayout()
|
||||||
self.vlay.addWidget(self.gerber_opt_group)
|
self.vlay.addWidget(self.gerber_opt_group)
|
||||||
self.vlay.addWidget(self.gerber_exp_group)
|
self.vlay.addWidget(self.gerber_exp_group)
|
||||||
|
self.vlay.addStretch()
|
||||||
|
|
||||||
self.layout.addWidget(self.gerber_gen_group)
|
self.layout.addWidget(self.gerber_gen_group)
|
||||||
self.layout.addLayout(self.vlay)
|
self.layout.addLayout(self.vlay)
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCSpinner, FCDoubleSpinner, RadioSet
|
from AppGUI.GUIElements import FCSpinner, FCDoubleSpinner, RadioSet
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, FCEntry
|
from AppGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, NumericalEvalTupleEntry
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -116,7 +116,7 @@ class Tools2CalPrefGroupUI(OptionsGroupUI):
|
|||||||
"(x, y) point will be used,")
|
"(x, y) point will be used,")
|
||||||
)
|
)
|
||||||
|
|
||||||
self.toolchange_xy_entry = FCEntry()
|
self.toolchange_xy_entry = NumericalEvalTupleEntry(border_color='#0069A9')
|
||||||
|
|
||||||
grid_lay.addWidget(toolchangexy_lbl, 7, 0)
|
grid_lay.addWidget(toolchangexy_lbl, 7, 0)
|
||||||
grid_lay.addWidget(self.toolchange_xy_entry, 7, 1, 1, 2)
|
grid_lay.addWidget(self.toolchange_xy_entry, 7, 1, 1, 2)
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCCheckBox, RadioSet, FCDoubleSpinner
|
from AppGUI.GUIElements import FCCheckBox, RadioSet, FCDoubleSpinner
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCDoubleSpinner, RadioSet
|
from AppGUI.GUIElements import FCDoubleSpinner, RadioSet
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCDoubleSpinner, RadioSet
|
from AppGUI.GUIElements import FCDoubleSpinner, RadioSet
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCSpinner
|
from AppGUI.GUIElements import FCSpinner
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.preferences.tools.Tools2InvertPrefGroupUI import Tools2InvertPrefGroupUI
|
from AppGUI.preferences.tools.Tools2InvertPrefGroupUI import Tools2InvertPrefGroupUI
|
||||||
from flatcamGUI.preferences.tools.Tools2PunchGerberPrefGroupUI import Tools2PunchGerberPrefGroupUI
|
from AppGUI.preferences.tools.Tools2PunchGerberPrefGroupUI import Tools2PunchGerberPrefGroupUI
|
||||||
from flatcamGUI.preferences.tools.Tools2EDrillsPrefGroupUI import Tools2EDrillsPrefGroupUI
|
from AppGUI.preferences.tools.Tools2EDrillsPrefGroupUI import Tools2EDrillsPrefGroupUI
|
||||||
from flatcamGUI.preferences.tools.Tools2CalPrefGroupUI import Tools2CalPrefGroupUI
|
from AppGUI.preferences.tools.Tools2CalPrefGroupUI import Tools2CalPrefGroupUI
|
||||||
from flatcamGUI.preferences.tools.Tools2FiducialsPrefGroupUI import Tools2FiducialsPrefGroupUI
|
from AppGUI.preferences.tools.Tools2FiducialsPrefGroupUI import Tools2FiducialsPrefGroupUI
|
||||||
from flatcamGUI.preferences.tools.Tools2CThievingPrefGroupUI import Tools2CThievingPrefGroupUI
|
from AppGUI.preferences.tools.Tools2CThievingPrefGroupUI import Tools2CThievingPrefGroupUI
|
||||||
from flatcamGUI.preferences.tools.Tools2QRCodePrefGroupUI import Tools2QRCodePrefGroupUI
|
from AppGUI.preferences.tools.Tools2QRCodePrefGroupUI import Tools2QRCodePrefGroupUI
|
||||||
from flatcamGUI.preferences.tools.Tools2OptimalPrefGroupUI import Tools2OptimalPrefGroupUI
|
from AppGUI.preferences.tools.Tools2OptimalPrefGroupUI import Tools2OptimalPrefGroupUI
|
||||||
from flatcamGUI.preferences.tools.Tools2RulesCheckPrefGroupUI import Tools2RulesCheckPrefGroupUI
|
from AppGUI.preferences.tools.Tools2RulesCheckPrefGroupUI import Tools2RulesCheckPrefGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCCheckBox, RadioSet, FCDoubleSpinner
|
from AppGUI.GUIElements import FCCheckBox, RadioSet, FCDoubleSpinner
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets, QtCore
|
from PyQt5 import QtWidgets, QtCore, QtGui
|
||||||
from PyQt5.QtCore import Qt, QSettings
|
from PyQt5.QtCore import Qt, QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCSpinner, RadioSet, FCTextArea, FCEntry
|
from AppGUI.GUIElements import FCSpinner, RadioSet, FCTextArea, FCEntry
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -205,3 +205,67 @@ class Tools2QRCodePrefGroupUI(OptionsGroupUI):
|
|||||||
grid_lay.addWidget(self.sel_limit_label, 11, 0)
|
grid_lay.addWidget(self.sel_limit_label, 11, 0)
|
||||||
grid_lay.addWidget(self.sel_limit_entry, 11, 1)
|
grid_lay.addWidget(self.sel_limit_entry, 11, 1)
|
||||||
# self.layout.addStretch()
|
# self.layout.addStretch()
|
||||||
|
|
||||||
|
# QRCode Tool
|
||||||
|
self.fill_color_entry.editingFinished.connect(self.on_qrcode_fill_color_entry)
|
||||||
|
self.fill_color_button.clicked.connect(self.on_qrcode_fill_color_button)
|
||||||
|
self.back_color_entry.editingFinished.connect(self.on_qrcode_back_color_entry)
|
||||||
|
self.back_color_button.clicked.connect(self.on_qrcode_back_color_button)
|
||||||
|
|
||||||
|
def on_qrcode_fill_color_entry(self):
|
||||||
|
self.app.defaults['tools_qrcode_fill_color'] = self.fill_color_entry.get_value()
|
||||||
|
self.fill_color_button.setStyleSheet(
|
||||||
|
"background-color:%s;"
|
||||||
|
"border-color: dimgray" % str(self.defaults['tools_qrcode_fill_color'])
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_qrcode_fill_color_button(self):
|
||||||
|
current_color = QtGui.QColor(self.app.defaults['tools_qrcode_fill_color'])
|
||||||
|
|
||||||
|
c_dialog = QtWidgets.QColorDialog()
|
||||||
|
fill_color = c_dialog.getColor(initial=current_color)
|
||||||
|
|
||||||
|
if fill_color.isValid() is False:
|
||||||
|
return
|
||||||
|
|
||||||
|
# if new color is different then mark that the Preferences are changed
|
||||||
|
if fill_color != current_color:
|
||||||
|
self.app.preferencesUiManager.on_preferences_edited()
|
||||||
|
|
||||||
|
self.fill_color_button.setStyleSheet(
|
||||||
|
"background-color:%s;"
|
||||||
|
"border-color: dimgray" % str(fill_color.name())
|
||||||
|
)
|
||||||
|
|
||||||
|
new_val_sel = str(fill_color.name())
|
||||||
|
self.fill_color_entry.set_value(new_val_sel)
|
||||||
|
self.app.defaults['tools_qrcode_fill_color'] = new_val_sel
|
||||||
|
|
||||||
|
def on_qrcode_back_color_entry(self):
|
||||||
|
self.app.defaults['tools_qrcode_back_color'] = self.back_color_entry.get_value()
|
||||||
|
self.back_color_button.setStyleSheet(
|
||||||
|
"background-color:%s;"
|
||||||
|
"border-color: dimgray" % str(self.defaults['tools_qrcode_back_color'])
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_qrcode_back_color_button(self):
|
||||||
|
current_color = QtGui.QColor(self.app.defaults['tools_qrcode_back_color'])
|
||||||
|
|
||||||
|
c_dialog = QtWidgets.QColorDialog()
|
||||||
|
back_color = c_dialog.getColor(initial=current_color)
|
||||||
|
|
||||||
|
if back_color.isValid() is False:
|
||||||
|
return
|
||||||
|
|
||||||
|
# if new color is different then mark that the Preferences are changed
|
||||||
|
if back_color != current_color:
|
||||||
|
self.app.preferencesUiManager.on_preferences_edited()
|
||||||
|
|
||||||
|
self.back_color_button.setStyleSheet(
|
||||||
|
"background-color:%s;"
|
||||||
|
"border-color: dimgray" % str(back_color.name())
|
||||||
|
)
|
||||||
|
|
||||||
|
new_val_sel = str(back_color.name())
|
||||||
|
self.back_color_entry.set_value(new_val_sel)
|
||||||
|
self.app.defaults['tools_qrcode_back_color'] = new_val_sel
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCCheckBox, FCDoubleSpinner
|
from AppGUI.GUIElements import FCCheckBox, FCDoubleSpinner
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCDoubleSpinner, RadioSet
|
from AppGUI.GUIElements import FCDoubleSpinner, RadioSet
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCDoubleSpinner
|
from AppGUI.GUIElements import FCDoubleSpinner
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
81
AppGUI/preferences/tools/ToolsCornersPrefGroupUI.py
Normal file
81
AppGUI/preferences/tools/ToolsCornersPrefGroupUI.py
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
from PyQt5 import QtWidgets
|
||||||
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
|
from AppGUI.GUIElements import FCDoubleSpinner
|
||||||
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
|
import gettext
|
||||||
|
import AppTranslation as fcTranslate
|
||||||
|
import builtins
|
||||||
|
|
||||||
|
fcTranslate.apply_language('strings')
|
||||||
|
if '_' not in builtins.__dict__:
|
||||||
|
_ = gettext.gettext
|
||||||
|
|
||||||
|
settings = QSettings("Open Source", "FlatCAM")
|
||||||
|
if settings.contains("machinist"):
|
||||||
|
machinist_setting = settings.value('machinist', type=int)
|
||||||
|
else:
|
||||||
|
machinist_setting = 0
|
||||||
|
|
||||||
|
|
||||||
|
class ToolsCornersPrefGroupUI(OptionsGroupUI):
|
||||||
|
def __init__(self, decimals=4, parent=None):
|
||||||
|
# OptionsGroupUI.__init__(self, "Calculators Tool Options", parent=parent)
|
||||||
|
super(ToolsCornersPrefGroupUI, self).__init__(self, parent=parent)
|
||||||
|
|
||||||
|
self.setTitle(str(_("Corner Markers Options")))
|
||||||
|
self.decimals = decimals
|
||||||
|
|
||||||
|
grid0 = QtWidgets.QGridLayout()
|
||||||
|
grid0.setColumnStretch(0, 0)
|
||||||
|
grid0.setColumnStretch(1, 1)
|
||||||
|
self.layout.addLayout(grid0)
|
||||||
|
|
||||||
|
self.param_label = QtWidgets.QLabel('<b>%s:</b>' % _('Parameters'))
|
||||||
|
self.param_label.setToolTip(
|
||||||
|
_("Parameters used for this tool.")
|
||||||
|
)
|
||||||
|
grid0.addWidget(self.param_label, 0, 0, 1, 2)
|
||||||
|
|
||||||
|
# Thickness #
|
||||||
|
self.thick_label = QtWidgets.QLabel('%s:' % _("Thickness"))
|
||||||
|
self.thick_label.setToolTip(
|
||||||
|
_("The thickness of the line that makes the corner marker.")
|
||||||
|
)
|
||||||
|
self.thick_entry = FCDoubleSpinner()
|
||||||
|
self.thick_entry.set_range(0.0000, 9.9999)
|
||||||
|
self.thick_entry.set_precision(self.decimals)
|
||||||
|
self.thick_entry.setWrapping(True)
|
||||||
|
self.thick_entry.setSingleStep(10 ** -self.decimals)
|
||||||
|
|
||||||
|
grid0.addWidget(self.thick_label, 1, 0)
|
||||||
|
grid0.addWidget(self.thick_entry, 1, 1)
|
||||||
|
|
||||||
|
# Length #
|
||||||
|
self.l_label = QtWidgets.QLabel('%s:' % _("Length"))
|
||||||
|
self.l_label.setToolTip(
|
||||||
|
_("The length of the line that makes the corner marker.")
|
||||||
|
)
|
||||||
|
self.l_entry = FCDoubleSpinner()
|
||||||
|
self.l_entry.set_range(-9999.9999, 9999.9999)
|
||||||
|
self.l_entry.set_precision(self.decimals)
|
||||||
|
self.l_entry.setSingleStep(10 ** -self.decimals)
|
||||||
|
|
||||||
|
# Margin #
|
||||||
|
self.margin_label = QtWidgets.QLabel('%s:' % _("Margin"))
|
||||||
|
self.margin_label.setToolTip(
|
||||||
|
_("Bounding box margin.")
|
||||||
|
)
|
||||||
|
self.margin_entry = FCDoubleSpinner()
|
||||||
|
self.margin_entry.set_range(-9999.9999, 9999.9999)
|
||||||
|
self.margin_entry.set_precision(self.decimals)
|
||||||
|
self.margin_entry.setSingleStep(0.1)
|
||||||
|
|
||||||
|
grid0.addWidget(self.margin_label, 2, 0)
|
||||||
|
grid0.addWidget(self.margin_entry, 2, 1)
|
||||||
|
|
||||||
|
grid0.addWidget(self.l_label, 4, 0)
|
||||||
|
grid0.addWidget(self.l_entry, 4, 1)
|
||||||
|
|
||||||
|
self.layout.addStretch()
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCDoubleSpinner, FCCheckBox, RadioSet, FCComboBox
|
from AppGUI.GUIElements import FCDoubleSpinner, FCCheckBox, RadioSet, FCComboBox
|
||||||
from flatcamGUI.preferences import machinist_setting
|
from AppGUI.preferences import machinist_setting
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets, QtCore
|
from PyQt5 import QtWidgets, QtCore, QtGui
|
||||||
from PyQt5.QtCore import Qt, QSettings
|
from PyQt5.QtCore import Qt, QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import RadioSet, FCEntry, FCDoubleSpinner, FCCheckBox, FCComboBox
|
from AppGUI.GUIElements import RadioSet, FCEntry, FCDoubleSpinner, FCCheckBox, FCComboBox
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -314,3 +314,35 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
|
|||||||
grid0.addWidget(self.pagesize_combo, 17, 1)
|
grid0.addWidget(self.pagesize_combo, 17, 1)
|
||||||
|
|
||||||
self.layout.addStretch()
|
self.layout.addStretch()
|
||||||
|
|
||||||
|
# Film Tool
|
||||||
|
self.film_color_entry.editingFinished.connect(self.on_film_color_entry)
|
||||||
|
self.film_color_button.clicked.connect(self.on_film_color_button)
|
||||||
|
|
||||||
|
def on_film_color_entry(self):
|
||||||
|
self.app.defaults['tools_film_color'] = self.film_color_entry.get_value()
|
||||||
|
self.film_color_button.setStyleSheet(
|
||||||
|
"background-color:%s;"
|
||||||
|
"border-color: dimgray" % str(self.defaults['tools_film_color'])
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_film_color_button(self):
|
||||||
|
current_color = QtGui.QColor(self.app.defaults['tools_film_color'])
|
||||||
|
|
||||||
|
c_dialog = QtWidgets.QColorDialog()
|
||||||
|
film_color = c_dialog.getColor(initial=current_color)
|
||||||
|
|
||||||
|
if film_color.isValid() is False:
|
||||||
|
return
|
||||||
|
|
||||||
|
# if new color is different then mark that the Preferences are changed
|
||||||
|
if film_color != current_color:
|
||||||
|
self.app.preferencesUiManager.on_preferences_edited()
|
||||||
|
|
||||||
|
self.film_color_button.setStyleSheet(
|
||||||
|
"background-color:%s;"
|
||||||
|
"border-color: dimgray" % str(film_color.name())
|
||||||
|
)
|
||||||
|
new_val_sel = str(film_color.name())
|
||||||
|
self.film_color_entry.set_value(new_val_sel)
|
||||||
|
self.app.defaults['tools_film_color'] = new_val_sel
|
||||||
319
AppGUI/preferences/tools/ToolsISOPrefGroupUI.py
Normal file
319
AppGUI/preferences/tools/ToolsISOPrefGroupUI.py
Normal file
@@ -0,0 +1,319 @@
|
|||||||
|
from PyQt5 import QtWidgets
|
||||||
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
|
from AppGUI.GUIElements import RadioSet, FCDoubleSpinner, FCComboBox, FCCheckBox, FCSpinner, NumericalEvalTupleEntry
|
||||||
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
|
import gettext
|
||||||
|
import AppTranslation as fcTranslate
|
||||||
|
import builtins
|
||||||
|
|
||||||
|
fcTranslate.apply_language('strings')
|
||||||
|
if '_' not in builtins.__dict__:
|
||||||
|
_ = gettext.gettext
|
||||||
|
|
||||||
|
settings = QSettings("Open Source", "FlatCAM")
|
||||||
|
if settings.contains("machinist"):
|
||||||
|
machinist_setting = settings.value('machinist', type=int)
|
||||||
|
else:
|
||||||
|
machinist_setting = 0
|
||||||
|
|
||||||
|
|
||||||
|
class ToolsISOPrefGroupUI(OptionsGroupUI):
|
||||||
|
def __init__(self, decimals=4, parent=None):
|
||||||
|
super(ToolsISOPrefGroupUI, self).__init__(self, parent=parent)
|
||||||
|
|
||||||
|
self.setTitle(str(_("Isolation Tool Options")))
|
||||||
|
self.decimals = decimals
|
||||||
|
|
||||||
|
# ## Clear non-copper regions
|
||||||
|
self.iso_label = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
|
||||||
|
self.iso_label.setToolTip(
|
||||||
|
_("Create a Geometry object with\n"
|
||||||
|
"toolpaths to cut around polygons.")
|
||||||
|
)
|
||||||
|
self.layout.addWidget(self.iso_label)
|
||||||
|
|
||||||
|
grid0 = QtWidgets.QGridLayout()
|
||||||
|
self.layout.addLayout(grid0)
|
||||||
|
|
||||||
|
# Tool Dias
|
||||||
|
isotdlabel = QtWidgets.QLabel('<b><font color="green">%s:</font></b>' % _('Tools Dia'))
|
||||||
|
isotdlabel.setToolTip(
|
||||||
|
_("Diameters of the tools, separated by comma.\n"
|
||||||
|
"The value of the diameter has to use the dot decimals separator.\n"
|
||||||
|
"Valid values: 0.3, 1.0")
|
||||||
|
)
|
||||||
|
self.tool_dia_entry = NumericalEvalTupleEntry(border_color='#0069A9')
|
||||||
|
self.tool_dia_entry.setPlaceholderText(_("Comma separated values"))
|
||||||
|
|
||||||
|
grid0.addWidget(isotdlabel, 0, 0)
|
||||||
|
grid0.addWidget(self.tool_dia_entry, 0, 1, 1, 2)
|
||||||
|
|
||||||
|
# Tool order Radio Button
|
||||||
|
self.order_label = QtWidgets.QLabel('%s:' % _('Tool order'))
|
||||||
|
self.order_label.setToolTip(_("This set the way that the tools in the tools table are used.\n"
|
||||||
|
"'No' --> means that the used order is the one in the tool table\n"
|
||||||
|
"'Forward' --> means that the tools will be ordered from small to big\n"
|
||||||
|
"'Reverse' --> means that the tools will ordered from big to small\n\n"
|
||||||
|
"WARNING: using rest machining will automatically set the order\n"
|
||||||
|
"in reverse and disable this control."))
|
||||||
|
|
||||||
|
self.order_radio = RadioSet([{'label': _('No'), 'value': 'no'},
|
||||||
|
{'label': _('Forward'), 'value': 'fwd'},
|
||||||
|
{'label': _('Reverse'), 'value': 'rev'}])
|
||||||
|
|
||||||
|
grid0.addWidget(self.order_label, 1, 0)
|
||||||
|
grid0.addWidget(self.order_radio, 1, 1, 1, 2)
|
||||||
|
|
||||||
|
# Tool Type Radio Button
|
||||||
|
self.tool_type_label = QtWidgets.QLabel('%s:' % _('Tool Type'))
|
||||||
|
self.tool_type_label.setToolTip(
|
||||||
|
_("Default tool type:\n"
|
||||||
|
"- 'V-shape'\n"
|
||||||
|
"- Circular")
|
||||||
|
)
|
||||||
|
|
||||||
|
self.tool_type_radio = RadioSet([{'label': _('V-shape'), 'value': 'V'},
|
||||||
|
{'label': _('Circular'), 'value': 'C1'}])
|
||||||
|
self.tool_type_radio.setToolTip(
|
||||||
|
_("Default tool type:\n"
|
||||||
|
"- 'V-shape'\n"
|
||||||
|
"- Circular")
|
||||||
|
)
|
||||||
|
|
||||||
|
grid0.addWidget(self.tool_type_label, 2, 0)
|
||||||
|
grid0.addWidget(self.tool_type_radio, 2, 1, 1, 2)
|
||||||
|
|
||||||
|
# Tip Dia
|
||||||
|
self.tipdialabel = QtWidgets.QLabel('%s:' % _('V-Tip Dia'))
|
||||||
|
self.tipdialabel.setToolTip(
|
||||||
|
_("The tip diameter for V-Shape Tool"))
|
||||||
|
self.tipdia_entry = FCDoubleSpinner()
|
||||||
|
self.tipdia_entry.set_precision(self.decimals)
|
||||||
|
self.tipdia_entry.set_range(0, 1000)
|
||||||
|
self.tipdia_entry.setSingleStep(0.1)
|
||||||
|
|
||||||
|
grid0.addWidget(self.tipdialabel, 3, 0)
|
||||||
|
grid0.addWidget(self.tipdia_entry, 3, 1, 1, 2)
|
||||||
|
|
||||||
|
# Tip Angle
|
||||||
|
self.tipanglelabel = QtWidgets.QLabel('%s:' % _('V-Tip Angle'))
|
||||||
|
self.tipanglelabel.setToolTip(
|
||||||
|
_("The tip angle for V-Shape Tool.\n"
|
||||||
|
"In degrees."))
|
||||||
|
self.tipangle_entry = FCDoubleSpinner()
|
||||||
|
self.tipangle_entry.set_precision(self.decimals)
|
||||||
|
self.tipangle_entry.set_range(1, 180)
|
||||||
|
self.tipangle_entry.setSingleStep(5)
|
||||||
|
self.tipangle_entry.setWrapping(True)
|
||||||
|
|
||||||
|
grid0.addWidget(self.tipanglelabel, 4, 0)
|
||||||
|
grid0.addWidget(self.tipangle_entry, 4, 1, 1, 2)
|
||||||
|
|
||||||
|
# Cut Z entry
|
||||||
|
cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
|
||||||
|
cutzlabel.setToolTip(
|
||||||
|
_("Depth of cut into material. Negative value.\n"
|
||||||
|
"In FlatCAM units.")
|
||||||
|
)
|
||||||
|
self.cutz_entry = FCDoubleSpinner()
|
||||||
|
self.cutz_entry.set_precision(self.decimals)
|
||||||
|
self.cutz_entry.set_range(-9999.9999, 0.0000)
|
||||||
|
self.cutz_entry.setSingleStep(0.1)
|
||||||
|
|
||||||
|
self.cutz_entry.setToolTip(
|
||||||
|
_("Depth of cut into material. Negative value.\n"
|
||||||
|
"In FlatCAM units.")
|
||||||
|
)
|
||||||
|
|
||||||
|
grid0.addWidget(cutzlabel, 5, 0)
|
||||||
|
grid0.addWidget(self.cutz_entry, 5, 1, 1, 2)
|
||||||
|
|
||||||
|
# New Diameter
|
||||||
|
self.newdialabel = QtWidgets.QLabel('%s:' % _('New Dia'))
|
||||||
|
self.newdialabel.setToolTip(
|
||||||
|
_("Diameter for the new tool to add in the Tool Table.\n"
|
||||||
|
"If the tool is V-shape type then this value is automatically\n"
|
||||||
|
"calculated from the other parameters.")
|
||||||
|
)
|
||||||
|
self.newdia_entry = FCDoubleSpinner()
|
||||||
|
self.newdia_entry.set_precision(self.decimals)
|
||||||
|
self.newdia_entry.set_range(0.0001, 9999.9999)
|
||||||
|
self.newdia_entry.setSingleStep(0.1)
|
||||||
|
|
||||||
|
grid0.addWidget(self.newdialabel, 6, 0)
|
||||||
|
grid0.addWidget(self.newdia_entry, 6, 1, 1, 2)
|
||||||
|
|
||||||
|
separator_line = QtWidgets.QFrame()
|
||||||
|
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||||
|
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||||
|
grid0.addWidget(separator_line, 7, 0, 1, 3)
|
||||||
|
|
||||||
|
# Passes
|
||||||
|
passlabel = QtWidgets.QLabel('%s:' % _('Passes'))
|
||||||
|
passlabel.setToolTip(
|
||||||
|
_("Width of the isolation gap in\n"
|
||||||
|
"number (integer) of tool widths.")
|
||||||
|
)
|
||||||
|
self.passes_entry = FCSpinner()
|
||||||
|
self.passes_entry.set_range(1, 999)
|
||||||
|
self.passes_entry.setObjectName("i_passes")
|
||||||
|
|
||||||
|
grid0.addWidget(passlabel, 8, 0)
|
||||||
|
grid0.addWidget(self.passes_entry, 8, 1, 1, 2)
|
||||||
|
|
||||||
|
# Overlap Entry
|
||||||
|
overlabel = QtWidgets.QLabel('%s:' % _('Overlap'))
|
||||||
|
overlabel.setToolTip(
|
||||||
|
_("How much (percentage) of the tool width to overlap each tool pass.")
|
||||||
|
)
|
||||||
|
self.overlap_entry = FCDoubleSpinner(suffix='%')
|
||||||
|
self.overlap_entry.set_precision(self.decimals)
|
||||||
|
self.overlap_entry.setWrapping(True)
|
||||||
|
self.overlap_entry.set_range(0.0000, 99.9999)
|
||||||
|
self.overlap_entry.setSingleStep(0.1)
|
||||||
|
self.overlap_entry.setObjectName("i_overlap")
|
||||||
|
|
||||||
|
grid0.addWidget(overlabel, 9, 0)
|
||||||
|
grid0.addWidget(self.overlap_entry, 9, 1, 1, 2)
|
||||||
|
|
||||||
|
# Milling Type Radio Button
|
||||||
|
self.milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type'))
|
||||||
|
self.milling_type_label.setToolTip(
|
||||||
|
_("Milling type when the selected tool is of type: 'iso_op':\n"
|
||||||
|
"- climb / best for precision milling and to reduce tool usage\n"
|
||||||
|
"- conventional / useful when there is no backlash compensation")
|
||||||
|
)
|
||||||
|
|
||||||
|
self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
|
||||||
|
{'label': _('Conventional'), 'value': 'cv'}])
|
||||||
|
self.milling_type_radio.setToolTip(
|
||||||
|
_("Milling type when the selected tool is of type: 'iso_op':\n"
|
||||||
|
"- climb / best for precision milling and to reduce tool usage\n"
|
||||||
|
"- conventional / useful when there is no backlash compensation")
|
||||||
|
)
|
||||||
|
|
||||||
|
grid0.addWidget(self.milling_type_label, 10, 0)
|
||||||
|
grid0.addWidget(self.milling_type_radio, 10, 1, 1, 2)
|
||||||
|
|
||||||
|
# Follow
|
||||||
|
self.follow_label = QtWidgets.QLabel('%s:' % _('Follow'))
|
||||||
|
self.follow_label.setToolTip(
|
||||||
|
_("Generate a 'Follow' geometry.\n"
|
||||||
|
"This means that it will cut through\n"
|
||||||
|
"the middle of the trace.")
|
||||||
|
)
|
||||||
|
|
||||||
|
self.follow_cb = FCCheckBox()
|
||||||
|
self.follow_cb.setToolTip(_("Generate a 'Follow' geometry.\n"
|
||||||
|
"This means that it will cut through\n"
|
||||||
|
"the middle of the trace."))
|
||||||
|
self.follow_cb.setObjectName("i_follow")
|
||||||
|
|
||||||
|
grid0.addWidget(self.follow_label, 11, 0)
|
||||||
|
grid0.addWidget(self.follow_cb, 11, 1, 1, 2)
|
||||||
|
|
||||||
|
# Isolation Type
|
||||||
|
self.iso_type_label = QtWidgets.QLabel('%s:' % _('Isolation Type'))
|
||||||
|
self.iso_type_label.setToolTip(
|
||||||
|
_("Choose how the isolation will be executed:\n"
|
||||||
|
"- 'Full' -> complete isolation of polygons\n"
|
||||||
|
"- 'Ext' -> will isolate only on the outside\n"
|
||||||
|
"- 'Int' -> will isolate only on the inside\n"
|
||||||
|
"'Exterior' isolation is almost always possible\n"
|
||||||
|
"(with the right tool) but 'Interior'\n"
|
||||||
|
"isolation can be done only when there is an opening\n"
|
||||||
|
"inside of the polygon (e.g polygon is a 'doughnut' shape).")
|
||||||
|
)
|
||||||
|
self.iso_type_radio = RadioSet([{'label': _('Full'), 'value': 'full'},
|
||||||
|
{'label': _('Ext'), 'value': 'ext'},
|
||||||
|
{'label': _('Int'), 'value': 'int'}])
|
||||||
|
self.iso_type_radio.setObjectName("i_type")
|
||||||
|
|
||||||
|
grid0.addWidget(self.iso_type_label, 12, 0)
|
||||||
|
grid0.addWidget(self.iso_type_radio, 12, 1, 1, 2)
|
||||||
|
|
||||||
|
separator_line = QtWidgets.QFrame()
|
||||||
|
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||||
|
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||||
|
grid0.addWidget(separator_line, 13, 0, 1, 3)
|
||||||
|
|
||||||
|
# Rest machining CheckBox
|
||||||
|
self.rest_cb = FCCheckBox('%s' % _("Rest"))
|
||||||
|
self.rest_cb.setObjectName("i_rest_machining")
|
||||||
|
self.rest_cb.setToolTip(
|
||||||
|
_("If checked, use 'rest machining'.\n"
|
||||||
|
"Basically it will isolate outside PCB features,\n"
|
||||||
|
"using the biggest tool and continue with the next tools,\n"
|
||||||
|
"from bigger to smaller, to isolate the copper features that\n"
|
||||||
|
"could not be cleared by previous tool, until there is\n"
|
||||||
|
"no more copper features to isolate or there are no more tools.\n"
|
||||||
|
"If not checked, use the standard algorithm.")
|
||||||
|
)
|
||||||
|
|
||||||
|
grid0.addWidget(self.rest_cb, 17, 0)
|
||||||
|
|
||||||
|
# Combine All Passes
|
||||||
|
self.combine_passes_cb = FCCheckBox(label=_('Combine'))
|
||||||
|
self.combine_passes_cb.setToolTip(
|
||||||
|
_("Combine all passes into one object")
|
||||||
|
)
|
||||||
|
self.combine_passes_cb.setObjectName("i_combine")
|
||||||
|
|
||||||
|
grid0.addWidget(self.combine_passes_cb, 17, 1)
|
||||||
|
|
||||||
|
# Exception Areas
|
||||||
|
self.except_cb = FCCheckBox(label=_('Except'))
|
||||||
|
self.except_cb.setToolTip(_("When the isolation geometry is generated,\n"
|
||||||
|
"by checking this, the area of the object below\n"
|
||||||
|
"will be subtracted from the isolation geometry."))
|
||||||
|
self.except_cb.setObjectName("i_except")
|
||||||
|
grid0.addWidget(self.except_cb, 17, 2)
|
||||||
|
|
||||||
|
# Isolation Scope
|
||||||
|
self.select_label = QtWidgets.QLabel('%s:' % _("Selection"))
|
||||||
|
self.select_label.setToolTip(
|
||||||
|
_("Isolation scope. Choose what to isolate:\n"
|
||||||
|
"- 'All' -> Isolate all the polygons in the object\n"
|
||||||
|
"- 'Selection' -> Isolate a selection of polygons.\n"
|
||||||
|
"- 'Reference Object' - will process the area specified by another object.")
|
||||||
|
)
|
||||||
|
self.select_combo = FCComboBox()
|
||||||
|
self.select_combo.addItems(
|
||||||
|
[_("All"), _("Area Selection"), _("Polygon Selection"), _("Reference Object")]
|
||||||
|
)
|
||||||
|
self.select_combo.setObjectName("i_selection")
|
||||||
|
|
||||||
|
grid0.addWidget(self.select_label, 20, 0)
|
||||||
|
grid0.addWidget(self.select_combo, 20, 1, 1, 2)
|
||||||
|
|
||||||
|
# Area Shape
|
||||||
|
self.area_shape_label = QtWidgets.QLabel('%s:' % _("Shape"))
|
||||||
|
self.area_shape_label.setToolTip(
|
||||||
|
_("The kind of selection shape used for area selection.")
|
||||||
|
)
|
||||||
|
|
||||||
|
self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'},
|
||||||
|
{'label': _("Polygon"), 'value': 'polygon'}])
|
||||||
|
|
||||||
|
grid0.addWidget(self.area_shape_label, 21, 0)
|
||||||
|
grid0.addWidget(self.area_shape_radio, 21, 1, 1, 2)
|
||||||
|
|
||||||
|
separator_line = QtWidgets.QFrame()
|
||||||
|
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||||
|
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||||
|
grid0.addWidget(separator_line, 22, 0, 1, 3)
|
||||||
|
|
||||||
|
# ## Plotting type
|
||||||
|
self.plotting_radio = RadioSet([{'label': _('Normal'), 'value': 'normal'},
|
||||||
|
{"label": _("Progressive"), "value": "progressive"}])
|
||||||
|
plotting_label = QtWidgets.QLabel('%s:' % _("Plotting"))
|
||||||
|
plotting_label.setToolTip(
|
||||||
|
_("- 'Normal' - normal plotting, done at the end of the job\n"
|
||||||
|
"- 'Progressive' - each shape is plotted after it is generated")
|
||||||
|
)
|
||||||
|
grid0.addWidget(plotting_label, 23, 0)
|
||||||
|
grid0.addWidget(self.plotting_radio, 23, 1, 1, 2)
|
||||||
|
|
||||||
|
self.layout.addStretch()
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCEntry, RadioSet, FCDoubleSpinner, FCComboBox, FCCheckBox
|
from AppGUI.GUIElements import RadioSet, FCDoubleSpinner, FCComboBox, FCCheckBox, NumericalEvalTupleEntry
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -45,7 +45,7 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI):
|
|||||||
"Valid values: 0.3, 1.0")
|
"Valid values: 0.3, 1.0")
|
||||||
)
|
)
|
||||||
grid0.addWidget(ncctdlabel, 0, 0)
|
grid0.addWidget(ncctdlabel, 0, 0)
|
||||||
self.ncc_tool_dia_entry = FCEntry(border_color='#0069A9')
|
self.ncc_tool_dia_entry = NumericalEvalTupleEntry(border_color='#0069A9')
|
||||||
self.ncc_tool_dia_entry.setPlaceholderText(_("Comma separated values"))
|
self.ncc_tool_dia_entry.setPlaceholderText(_("Comma separated values"))
|
||||||
grid0.addWidget(self.ncc_tool_dia_entry, 0, 1)
|
grid0.addWidget(self.ncc_tool_dia_entry, 0, 1)
|
||||||
|
|
||||||
@@ -227,7 +227,7 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI):
|
|||||||
# ], orientation='vertical', stretch=False)
|
# ], orientation='vertical', stretch=False)
|
||||||
self.ncc_method_combo = FCComboBox()
|
self.ncc_method_combo = FCComboBox()
|
||||||
self.ncc_method_combo.addItems(
|
self.ncc_method_combo.addItems(
|
||||||
[_("Standard"), _("Seed"), _("Lines")]
|
[_("Standard"), _("Seed"), _("Lines"), _("Combo")]
|
||||||
)
|
)
|
||||||
|
|
||||||
grid0.addWidget(methodlabel, 12, 0)
|
grid0.addWidget(methodlabel, 12, 0)
|
||||||
@@ -285,7 +285,7 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI):
|
|||||||
grid0.addWidget(separator_line, 16, 0, 1, 2)
|
grid0.addWidget(separator_line, 16, 0, 1, 2)
|
||||||
|
|
||||||
# Rest machining CheckBox
|
# Rest machining CheckBox
|
||||||
self.ncc_rest_cb = FCCheckBox('%s' % _("Rest Machining"))
|
self.ncc_rest_cb = FCCheckBox('%s' % _("Rest"))
|
||||||
self.ncc_rest_cb.setToolTip(
|
self.ncc_rest_cb.setToolTip(
|
||||||
_("If checked, use 'rest machining'.\n"
|
_("If checked, use 'rest machining'.\n"
|
||||||
"Basically it will clear copper outside PCB features,\n"
|
"Basically it will clear copper outside PCB features,\n"
|
||||||
@@ -336,14 +336,14 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI):
|
|||||||
grid0.addWidget(separator_line, 20, 0, 1, 2)
|
grid0.addWidget(separator_line, 20, 0, 1, 2)
|
||||||
|
|
||||||
# ## Plotting type
|
# ## Plotting type
|
||||||
self.ncc_plotting_radio = RadioSet([{'label': _('Normal'), 'value': 'normal'},
|
self.plotting_radio = RadioSet([{'label': _('Normal'), 'value': 'normal'},
|
||||||
{"label": _("Progressive"), "value": "progressive"}])
|
{"label": _("Progressive"), "value": "progressive"}])
|
||||||
plotting_label = QtWidgets.QLabel('%s:' % _("NCC Plotting"))
|
plotting_label = QtWidgets.QLabel('%s:' % _("Plotting"))
|
||||||
plotting_label.setToolTip(
|
plotting_label.setToolTip(
|
||||||
_("- 'Normal' - normal plotting, done at the end of the NCC job\n"
|
_("- 'Normal' - normal plotting, done at the end of the job\n"
|
||||||
"- 'Progressive' - after each shape is generated it will be plotted.")
|
"- 'Progressive' - each shape is plotted after it is generated")
|
||||||
)
|
)
|
||||||
grid0.addWidget(plotting_label, 21, 0)
|
grid0.addWidget(plotting_label, 21, 0)
|
||||||
grid0.addWidget(self.ncc_plotting_radio, 21, 1)
|
grid0.addWidget(self.plotting_radio, 21, 1)
|
||||||
|
|
||||||
self.layout.addStretch()
|
self.layout.addStretch()
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCEntry, RadioSet, FCDoubleSpinner, FCComboBox, FCCheckBox
|
from AppGUI.GUIElements import RadioSet, FCDoubleSpinner, FCComboBox, FCCheckBox, NumericalEvalTupleEntry
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -53,7 +53,7 @@ class ToolsPaintPrefGroupUI(OptionsGroupUI):
|
|||||||
)
|
)
|
||||||
grid0.addWidget(ptdlabel, 0, 0)
|
grid0.addWidget(ptdlabel, 0, 0)
|
||||||
|
|
||||||
self.painttooldia_entry = FCEntry(border_color='#0069A9')
|
self.painttooldia_entry = NumericalEvalTupleEntry(border_color='#0069A9')
|
||||||
self.painttooldia_entry.setPlaceholderText(_("Comma separated values"))
|
self.painttooldia_entry.setPlaceholderText(_("Comma separated values"))
|
||||||
|
|
||||||
grid0.addWidget(self.painttooldia_entry, 0, 1)
|
grid0.addWidget(self.painttooldia_entry, 0, 1)
|
||||||
@@ -241,8 +241,8 @@ class ToolsPaintPrefGroupUI(OptionsGroupUI):
|
|||||||
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||||
grid0.addWidget(separator_line, 13, 0, 1, 2)
|
grid0.addWidget(separator_line, 13, 0, 1, 2)
|
||||||
|
|
||||||
self.rest_cb = FCCheckBox('%s' % _("Rest Machining"))
|
self.rest_cb = FCCheckBox('%s' % _("Rest"))
|
||||||
self.rest_cb.setObjectName(_("Rest Machining"))
|
self.rest_cb.setObjectName(_("Rest"))
|
||||||
self.rest_cb.setToolTip(
|
self.rest_cb.setToolTip(
|
||||||
_("If checked, use 'rest machining'.\n"
|
_("If checked, use 'rest machining'.\n"
|
||||||
"Basically it will clear copper outside PCB features,\n"
|
"Basically it will clear copper outside PCB features,\n"
|
||||||
@@ -277,7 +277,7 @@ class ToolsPaintPrefGroupUI(OptionsGroupUI):
|
|||||||
# )
|
# )
|
||||||
self.selectmethod_combo = FCComboBox()
|
self.selectmethod_combo = FCComboBox()
|
||||||
self.selectmethod_combo.addItems(
|
self.selectmethod_combo.addItems(
|
||||||
[_("Polygon Selection"), _("Area Selection"), _("All Polygons"), _("Reference Object")]
|
[_("Polygon Selection"), _("Area Selection"), _("All"), _("Reference Object")]
|
||||||
)
|
)
|
||||||
|
|
||||||
grid0.addWidget(selectlabel, 15, 0)
|
grid0.addWidget(selectlabel, 15, 0)
|
||||||
@@ -302,10 +302,10 @@ class ToolsPaintPrefGroupUI(OptionsGroupUI):
|
|||||||
# ## Plotting type
|
# ## Plotting type
|
||||||
self.paint_plotting_radio = RadioSet([{'label': _('Normal'), 'value': 'normal'},
|
self.paint_plotting_radio = RadioSet([{'label': _('Normal'), 'value': 'normal'},
|
||||||
{"label": _("Progressive"), "value": "progressive"}])
|
{"label": _("Progressive"), "value": "progressive"}])
|
||||||
plotting_label = QtWidgets.QLabel('%s:' % _("Paint Plotting"))
|
plotting_label = QtWidgets.QLabel('%s:' % _("Plotting"))
|
||||||
plotting_label.setToolTip(
|
plotting_label.setToolTip(
|
||||||
_("- 'Normal' - normal plotting, done at the end of the Paint job\n"
|
_("- 'Normal' - normal plotting, done at the end of the job\n"
|
||||||
"- 'Progressive' - after each shape is generated it will be plotted.")
|
"- 'Progressive' - each shape is plotted after it is generated")
|
||||||
)
|
)
|
||||||
grid0.addWidget(plotting_label, 20, 0)
|
grid0.addWidget(plotting_label, 20, 0)
|
||||||
grid0.addWidget(self.paint_plotting_radio, 20, 1)
|
grid0.addWidget(self.paint_plotting_radio, 20, 1)
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCDoubleSpinner, FCSpinner, RadioSet, FCCheckBox
|
from AppGUI.GUIElements import FCDoubleSpinner, FCSpinner, RadioSet, FCCheckBox
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -1,19 +1,22 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.preferences.tools.ToolsSubPrefGroupUI import ToolsSubPrefGroupUI
|
from AppGUI.preferences.tools.ToolsSubPrefGroupUI import ToolsSubPrefGroupUI
|
||||||
from flatcamGUI.preferences.tools.ToolsSolderpastePrefGroupUI import ToolsSolderpastePrefGroupUI
|
from AppGUI.preferences.tools.ToolsSolderpastePrefGroupUI import ToolsSolderpastePrefGroupUI
|
||||||
from flatcamGUI.preferences.tools.ToolsTransformPrefGroupUI import ToolsTransformPrefGroupUI
|
from AppGUI.preferences.tools.ToolsCornersPrefGroupUI import ToolsCornersPrefGroupUI
|
||||||
from flatcamGUI.preferences.tools.ToolsCalculatorsPrefGroupUI import ToolsCalculatorsPrefGroupUI
|
from AppGUI.preferences.tools.ToolsTransformPrefGroupUI import ToolsTransformPrefGroupUI
|
||||||
from flatcamGUI.preferences.tools.ToolsPanelizePrefGroupUI import ToolsPanelizePrefGroupUI
|
from AppGUI.preferences.tools.ToolsCalculatorsPrefGroupUI import ToolsCalculatorsPrefGroupUI
|
||||||
from flatcamGUI.preferences.tools.ToolsFilmPrefGroupUI import ToolsFilmPrefGroupUI
|
from AppGUI.preferences.tools.ToolsPanelizePrefGroupUI import ToolsPanelizePrefGroupUI
|
||||||
from flatcamGUI.preferences.tools.ToolsPaintPrefGroupUI import ToolsPaintPrefGroupUI
|
from AppGUI.preferences.tools.ToolsFilmPrefGroupUI import ToolsFilmPrefGroupUI
|
||||||
from flatcamGUI.preferences.tools.Tools2sidedPrefGroupUI import Tools2sidedPrefGroupUI
|
from AppGUI.preferences.tools.Tools2sidedPrefGroupUI import Tools2sidedPrefGroupUI
|
||||||
from flatcamGUI.preferences.tools.ToolsCutoutPrefGroupUI import ToolsCutoutPrefGroupUI
|
|
||||||
from flatcamGUI.preferences.tools.ToolsNCCPrefGroupUI import ToolsNCCPrefGroupUI
|
from AppGUI.preferences.tools.ToolsCutoutPrefGroupUI import ToolsCutoutPrefGroupUI
|
||||||
|
from AppGUI.preferences.tools.ToolsNCCPrefGroupUI import ToolsNCCPrefGroupUI
|
||||||
|
from AppGUI.preferences.tools.ToolsPaintPrefGroupUI import ToolsPaintPrefGroupUI
|
||||||
|
from AppGUI.preferences.tools.ToolsISOPrefGroupUI import ToolsISOPrefGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -35,6 +38,9 @@ class ToolsPreferencesUI(QtWidgets.QWidget):
|
|||||||
self.setLayout(self.layout)
|
self.setLayout(self.layout)
|
||||||
self.decimals = decimals
|
self.decimals = decimals
|
||||||
|
|
||||||
|
self.tools_iso_group = ToolsISOPrefGroupUI(decimals=self.decimals)
|
||||||
|
self.tools_iso_group.setMinimumWidth(220)
|
||||||
|
|
||||||
self.tools_ncc_group = ToolsNCCPrefGroupUI(decimals=self.decimals)
|
self.tools_ncc_group = ToolsNCCPrefGroupUI(decimals=self.decimals)
|
||||||
self.tools_ncc_group.setMinimumWidth(220)
|
self.tools_ncc_group.setMinimumWidth(220)
|
||||||
|
|
||||||
@@ -62,6 +68,9 @@ class ToolsPreferencesUI(QtWidgets.QWidget):
|
|||||||
self.tools_solderpaste_group = ToolsSolderpastePrefGroupUI(decimals=self.decimals)
|
self.tools_solderpaste_group = ToolsSolderpastePrefGroupUI(decimals=self.decimals)
|
||||||
self.tools_solderpaste_group.setMinimumWidth(200)
|
self.tools_solderpaste_group.setMinimumWidth(200)
|
||||||
|
|
||||||
|
self.tools_corners_group = ToolsCornersPrefGroupUI(decimals=self.decimals)
|
||||||
|
self.tools_corners_group.setMinimumWidth(200)
|
||||||
|
|
||||||
self.tools_sub_group = ToolsSubPrefGroupUI(decimals=self.decimals)
|
self.tools_sub_group = ToolsSubPrefGroupUI(decimals=self.decimals)
|
||||||
self.tools_sub_group.setMinimumWidth(200)
|
self.tools_sub_group.setMinimumWidth(200)
|
||||||
|
|
||||||
@@ -71,7 +80,7 @@ class ToolsPreferencesUI(QtWidgets.QWidget):
|
|||||||
|
|
||||||
self.vlay1 = QtWidgets.QVBoxLayout()
|
self.vlay1 = QtWidgets.QVBoxLayout()
|
||||||
self.vlay1.addWidget(self.tools_paint_group)
|
self.vlay1.addWidget(self.tools_paint_group)
|
||||||
self.vlay1.addWidget(self.tools_panelize_group)
|
self.vlay1.addWidget(self.tools_iso_group)
|
||||||
|
|
||||||
self.vlay2 = QtWidgets.QVBoxLayout()
|
self.vlay2 = QtWidgets.QVBoxLayout()
|
||||||
self.vlay2.addWidget(self.tools_transform_group)
|
self.vlay2.addWidget(self.tools_transform_group)
|
||||||
@@ -84,6 +93,8 @@ class ToolsPreferencesUI(QtWidgets.QWidget):
|
|||||||
|
|
||||||
self.vlay4 = QtWidgets.QVBoxLayout()
|
self.vlay4 = QtWidgets.QVBoxLayout()
|
||||||
self.vlay4.addWidget(self.tools_solderpaste_group)
|
self.vlay4.addWidget(self.tools_solderpaste_group)
|
||||||
|
self.vlay4.addWidget(self.tools_corners_group)
|
||||||
|
self.vlay4.addWidget(self.tools_panelize_group)
|
||||||
|
|
||||||
self.layout.addLayout(self.vlay)
|
self.layout.addLayout(self.vlay)
|
||||||
self.layout.addLayout(self.vlay1)
|
self.layout.addLayout(self.vlay1)
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCEntry, FCDoubleSpinner, FCSpinner, FCComboBox
|
from AppGUI.GUIElements import FCDoubleSpinner, FCSpinner, FCComboBox, NumericalEvalTupleEntry
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -45,7 +45,7 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI):
|
|||||||
"The value of the diameter has to use the dot decimals separator.\n"
|
"The value of the diameter has to use the dot decimals separator.\n"
|
||||||
"Valid values: 0.3, 1.0")
|
"Valid values: 0.3, 1.0")
|
||||||
)
|
)
|
||||||
self.nozzle_tool_dia_entry = FCEntry()
|
self.nozzle_tool_dia_entry = NumericalEvalTupleEntry(border_color='#0069A9')
|
||||||
|
|
||||||
grid0.addWidget(nozzletdlabel, 0, 0)
|
grid0.addWidget(nozzletdlabel, 0, 0)
|
||||||
grid0.addWidget(self.nozzle_tool_dia_entry, 0, 1)
|
grid0.addWidget(self.nozzle_tool_dia_entry, 0, 1)
|
||||||
@@ -130,7 +130,7 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI):
|
|||||||
grid0.addWidget(self.z_toolchange_entry, 6, 1)
|
grid0.addWidget(self.z_toolchange_entry, 6, 1)
|
||||||
|
|
||||||
# X,Y Toolchange location
|
# X,Y Toolchange location
|
||||||
self.xy_toolchange_entry = FCEntry()
|
self.xy_toolchange_entry = NumericalEvalTupleEntry(border_color='#0069A9')
|
||||||
self.xy_toolchange_label = QtWidgets.QLabel('%s:' % _("Toolchange X-Y"))
|
self.xy_toolchange_label = QtWidgets.QLabel('%s:' % _("Toolchange X-Y"))
|
||||||
self.xy_toolchange_label.setToolTip(
|
self.xy_toolchange_label.setToolTip(
|
||||||
_("The X,Y location for tool (nozzle) change.\n"
|
_("The X,Y location for tool (nozzle) change.\n"
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCCheckBox
|
from AppGUI.GUIElements import FCCheckBox
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCDoubleSpinner, FCCheckBox, EvalEntry2
|
from AppGUI.GUIElements import FCDoubleSpinner, FCCheckBox, NumericalEvalTupleEntry
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -191,7 +191,7 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI):
|
|||||||
"The 'x' in (x, y) will be used when using Flip on X and\n"
|
"The 'x' in (x, y) will be used when using Flip on X and\n"
|
||||||
"the 'y' in (x, y) will be used when using Flip on Y and")
|
"the 'y' in (x, y) will be used when using Flip on Y and")
|
||||||
)
|
)
|
||||||
self.flip_ref_entry = EvalEntry2("(0, 0)")
|
self.flip_ref_entry = NumericalEvalTupleEntry(border_color='#0069A9')
|
||||||
|
|
||||||
grid0.addWidget(self.flip_ref_label, 14, 0, 1, 2)
|
grid0.addWidget(self.flip_ref_label, 14, 0, 1, 2)
|
||||||
grid0.addWidget(self.flip_ref_entry, 15, 0, 1, 2)
|
grid0.addWidget(self.flip_ref_entry, 15, 0, 1, 2)
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets, QtGui
|
from PyQt5 import QtWidgets, QtGui
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCButton, FCTextArea, FCEntry
|
from AppGUI.GUIElements import FCButton, FCTextArea, FCEntry
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets, QtGui
|
from PyQt5 import QtWidgets, QtGui
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import VerticalScrollArea, FCButton, FCTextArea, FCEntry
|
from AppGUI.GUIElements import VerticalScrollArea, FCButton, FCTextArea, FCEntry
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets, QtGui
|
from PyQt5 import QtWidgets, QtGui
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCButton, FCTextArea, FCEntry
|
from AppGUI.GUIElements import FCButton, FCTextArea, FCEntry
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from PyQt5 import QtWidgets, QtGui
|
from PyQt5 import QtWidgets, QtGui
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
from flatcamGUI.GUIElements import FCButton, FCTextArea, FCEntry
|
from AppGUI.GUIElements import FCButton, FCTextArea, FCEntry
|
||||||
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
|
|
||||||
from flatcamGUI.preferences.utilities.AutoCompletePrefGroupUI import AutoCompletePrefGroupUI
|
from AppGUI.preferences.utilities.AutoCompletePrefGroupUI import AutoCompletePrefGroupUI
|
||||||
from flatcamGUI.preferences.utilities.FAGrbPrefGroupUI import FAGrbPrefGroupUI
|
from AppGUI.preferences.utilities.FAGrbPrefGroupUI import FAGrbPrefGroupUI
|
||||||
from flatcamGUI.preferences.utilities.FAGcoPrefGroupUI import FAGcoPrefGroupUI
|
from AppGUI.preferences.utilities.FAGcoPrefGroupUI import FAGcoPrefGroupUI
|
||||||
from flatcamGUI.preferences.utilities.FAExcPrefGroupUI import FAExcPrefGroupUI
|
from AppGUI.preferences.utilities.FAExcPrefGroupUI import FAExcPrefGroupUI
|
||||||
|
|
||||||
|
|
||||||
class UtilPreferencesUI(QtWidgets.QWidget):
|
class UtilPreferencesUI(QtWidgets.QWidget):
|
||||||
394
AppObjects/AppObject.py
Normal file
394
AppObjects/AppObject.py
Normal file
@@ -0,0 +1,394 @@
|
|||||||
|
# ###########################################################
|
||||||
|
# FlatCAM: 2D Post-processing for Manufacturing #
|
||||||
|
# http://flatcam.org #
|
||||||
|
# Author: Juan Pablo Caram (c) #
|
||||||
|
# Date: 2/5/2014 #
|
||||||
|
# MIT Licence #
|
||||||
|
# Modified by Marius Stanciu (2020) #
|
||||||
|
# ###########################################################
|
||||||
|
|
||||||
|
from PyQt5 import QtCore
|
||||||
|
from AppObjects.ObjectCollection import *
|
||||||
|
from AppObjects.FlatCAMCNCJob import CNCJobObject
|
||||||
|
from AppObjects.FlatCAMDocument import DocumentObject
|
||||||
|
from AppObjects.FlatCAMExcellon import ExcellonObject
|
||||||
|
from AppObjects.FlatCAMGeometry import GeometryObject
|
||||||
|
from AppObjects.FlatCAMGerber import GerberObject
|
||||||
|
from AppObjects.FlatCAMScript import ScriptObject
|
||||||
|
|
||||||
|
import time
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
# FlatCAM Translation
|
||||||
|
import gettext
|
||||||
|
import AppTranslation as fcTranslate
|
||||||
|
import builtins
|
||||||
|
|
||||||
|
fcTranslate.apply_language('strings')
|
||||||
|
if '_' not in builtins.__dict__:
|
||||||
|
_ = gettext.gettext
|
||||||
|
|
||||||
|
|
||||||
|
class AppObject(QtCore.QObject):
|
||||||
|
|
||||||
|
# Emitted by app_obj.new_object() and passes the new object as argument, plot flag.
|
||||||
|
# on_object_created() adds the object to the collection, plots on appropriate flag
|
||||||
|
# and emits app_obj.new_object_available.
|
||||||
|
object_created = QtCore.pyqtSignal(object, bool, bool)
|
||||||
|
|
||||||
|
# Emitted when a object has been changed (like scaled, mirrored)
|
||||||
|
object_changed = QtCore.pyqtSignal(object)
|
||||||
|
|
||||||
|
# Emitted after object has been plotted.
|
||||||
|
# Calls 'on_zoom_fit' method to fit object in scene view in main thread to prevent drawing glitches.
|
||||||
|
object_plotted = QtCore.pyqtSignal(object)
|
||||||
|
|
||||||
|
plots_updated = QtCore.pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self, app):
|
||||||
|
super(AppObject, self).__init__()
|
||||||
|
self.app = app
|
||||||
|
self.inform = app.inform
|
||||||
|
|
||||||
|
# signals that are emitted when object state changes
|
||||||
|
self.object_created.connect(self.on_object_created)
|
||||||
|
self.object_changed.connect(self.on_object_changed)
|
||||||
|
self.object_plotted.connect(self.on_object_plotted)
|
||||||
|
self.plots_updated.connect(self.app.on_plots_updated)
|
||||||
|
|
||||||
|
def new_object(self, kind, name, initialize, plot=True, autoselected=True):
|
||||||
|
"""
|
||||||
|
Creates a new specialized FlatCAMObj and attaches it to the application,
|
||||||
|
this is, updates the GUI accordingly, any other records and plots it.
|
||||||
|
This method is thread-safe.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
* If the name is in use, the self.collection will modify it
|
||||||
|
when appending it to the collection. There is no need to handle
|
||||||
|
name conflicts here.
|
||||||
|
|
||||||
|
:param kind: The kind of object to create. One of 'gerber', 'excellon', 'cncjob' and 'geometry'.
|
||||||
|
:type kind: str
|
||||||
|
:param name: Name for the object.
|
||||||
|
:type name: str
|
||||||
|
:param initialize: Function to run after creation of the object but before it is attached to the application.
|
||||||
|
The function is called with 2 parameters: the new object and the App instance.
|
||||||
|
:type initialize: function
|
||||||
|
:param plot: If to plot the resulting object
|
||||||
|
:param autoselected: if the resulting object is autoselected in the Project tab and therefore in the
|
||||||
|
self.collection
|
||||||
|
:return: None
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
|
|
||||||
|
log.debug("AppObject.new_object()")
|
||||||
|
obj_plot = plot
|
||||||
|
obj_autoselected = autoselected
|
||||||
|
|
||||||
|
t0 = time.time() # Debug
|
||||||
|
|
||||||
|
# ## Create object
|
||||||
|
classdict = {
|
||||||
|
"gerber": GerberObject,
|
||||||
|
"excellon": ExcellonObject,
|
||||||
|
"cncjob": CNCJobObject,
|
||||||
|
"geometry": GeometryObject,
|
||||||
|
"script": ScriptObject,
|
||||||
|
"document": DocumentObject
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("Calling object constructor...")
|
||||||
|
|
||||||
|
# Object creation/instantiation
|
||||||
|
obj = classdict[kind](name)
|
||||||
|
|
||||||
|
obj.units = self.app.options["units"]
|
||||||
|
|
||||||
|
# IMPORTANT
|
||||||
|
# The key names in defaults and options dictionary's are not random:
|
||||||
|
# they have to have in name first the type of the object (geometry, excellon, cncjob and gerber) or how it's
|
||||||
|
# called here, the 'kind' followed by an underline. Above the App default values from self.defaults are
|
||||||
|
# copied to self.options. After that, below, depending on the type of
|
||||||
|
# object that is created, it will strip the name of the object and the underline (if the original key was
|
||||||
|
# let's say "excellon_toolchange", it will strip the excellon_) and to the obj.options the key will become
|
||||||
|
# "toolchange"
|
||||||
|
|
||||||
|
for option in self.app.options:
|
||||||
|
if option.find(kind + "_") == 0:
|
||||||
|
oname = option[len(kind) + 1:]
|
||||||
|
obj.options[oname] = self.app.options[option]
|
||||||
|
|
||||||
|
obj.isHovering = False
|
||||||
|
obj.notHovering = True
|
||||||
|
|
||||||
|
# Initialize as per user request
|
||||||
|
# User must take care to implement initialize
|
||||||
|
# in a thread-safe way as is is likely that we
|
||||||
|
# have been invoked in a separate thread.
|
||||||
|
t1 = time.time()
|
||||||
|
log.debug("%f seconds before initialize()." % (t1 - t0))
|
||||||
|
try:
|
||||||
|
return_value = initialize(obj, self.app)
|
||||||
|
except Exception as e:
|
||||||
|
msg = '[ERROR_NOTCL] %s' % _("An internal error has occurred. See shell.\n")
|
||||||
|
msg += _("Object ({kind}) failed because: {error} \n\n").format(kind=kind, error=str(e))
|
||||||
|
msg += traceback.format_exc()
|
||||||
|
self.app.inform.emit(msg)
|
||||||
|
return "fail"
|
||||||
|
|
||||||
|
t2 = time.time()
|
||||||
|
log.debug("%f seconds executing initialize()." % (t2 - t1))
|
||||||
|
|
||||||
|
if return_value == 'fail':
|
||||||
|
log.debug("Object (%s) parsing and/or geometry creation failed." % kind)
|
||||||
|
return "fail"
|
||||||
|
|
||||||
|
# Check units and convert if necessary
|
||||||
|
# This condition CAN be true because initialize() can change obj.units
|
||||||
|
if self.app.options["units"].upper() != obj.units.upper():
|
||||||
|
self.app.inform.emit('%s: %s' % (_("Converting units to "), self.app.options["units"]))
|
||||||
|
obj.convert_units(self.app.options["units"])
|
||||||
|
t3 = time.time()
|
||||||
|
log.debug("%f seconds converting units." % (t3 - t2))
|
||||||
|
|
||||||
|
# Create the bounding box for the object and then add the results to the obj.options
|
||||||
|
# But not for Scripts or for Documents
|
||||||
|
if kind != 'document' and kind != 'script':
|
||||||
|
try:
|
||||||
|
xmin, ymin, xmax, ymax = obj.bounds()
|
||||||
|
obj.options['xmin'] = xmin
|
||||||
|
obj.options['ymin'] = ymin
|
||||||
|
obj.options['xmax'] = xmax
|
||||||
|
obj.options['ymax'] = ymax
|
||||||
|
except Exception as e:
|
||||||
|
log.warning("AppObject.new_object() -> The object has no bounds properties. %s" % str(e))
|
||||||
|
return "fail"
|
||||||
|
|
||||||
|
try:
|
||||||
|
if kind == 'excellon':
|
||||||
|
obj.fill_color = self.app.defaults["excellon_plot_fill"]
|
||||||
|
obj.outline_color = self.app.defaults["excellon_plot_line"]
|
||||||
|
|
||||||
|
if kind == 'gerber':
|
||||||
|
obj.fill_color = self.app.defaults["gerber_plot_fill"]
|
||||||
|
obj.outline_color = self.app.defaults["gerber_plot_line"]
|
||||||
|
except Exception as e:
|
||||||
|
log.warning("AppObject.new_object() -> setting colors error. %s" % str(e))
|
||||||
|
|
||||||
|
# update the KeyWords list with the name of the file
|
||||||
|
self.app.myKeywords.append(obj.options['name'])
|
||||||
|
|
||||||
|
log.debug("Moving new object back to main thread.")
|
||||||
|
|
||||||
|
# Move the object to the main thread and let the app know that it is available.
|
||||||
|
obj.moveToThread(self.app.main_thread)
|
||||||
|
self.object_created.emit(obj, obj_plot, obj_autoselected)
|
||||||
|
|
||||||
|
return obj
|
||||||
|
|
||||||
|
def new_excellon_object(self):
|
||||||
|
"""
|
||||||
|
Creates a new, blank Excellon object.
|
||||||
|
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.new_object('excellon', 'new_exc', lambda x, y: None, plot=False)
|
||||||
|
|
||||||
|
def new_geometry_object(self):
|
||||||
|
"""
|
||||||
|
Creates a new, blank and single-tool Geometry object.
|
||||||
|
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
|
||||||
|
def initialize(obj, app):
|
||||||
|
obj.multitool = False
|
||||||
|
|
||||||
|
self.new_object('geometry', 'new_geo', initialize, plot=False)
|
||||||
|
|
||||||
|
def new_gerber_object(self):
|
||||||
|
"""
|
||||||
|
Creates a new, blank Gerber object.
|
||||||
|
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
|
||||||
|
def initialize(grb_obj, app):
|
||||||
|
grb_obj.multitool = False
|
||||||
|
grb_obj.source_file = []
|
||||||
|
grb_obj.multigeo = False
|
||||||
|
grb_obj.follow = False
|
||||||
|
grb_obj.apertures = {}
|
||||||
|
grb_obj.solid_geometry = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
grb_obj.options['xmin'] = 0
|
||||||
|
grb_obj.options['ymin'] = 0
|
||||||
|
grb_obj.options['xmax'] = 0
|
||||||
|
grb_obj.options['ymax'] = 0
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.new_object('gerber', 'new_grb', initialize, plot=False)
|
||||||
|
|
||||||
|
def new_script_object(self):
|
||||||
|
"""
|
||||||
|
Creates a new, blank TCL Script object.
|
||||||
|
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
|
||||||
|
# commands_list = "# AddCircle, AddPolygon, AddPolyline, AddRectangle, AlignDrill, " \
|
||||||
|
# "AlignDrillGrid, Bbox, Bounds, ClearShell, CopperClear,\n" \
|
||||||
|
# "# Cncjob, Cutout, Delete, Drillcncjob, ExportDXF, ExportExcellon, ExportGcode,\n" \
|
||||||
|
# "# ExportGerber, ExportSVG, Exteriors, Follow, GeoCutout, GeoUnion, GetNames,\n" \
|
||||||
|
# "# GetSys, ImportSvg, Interiors, Isolate, JoinExcellon, JoinGeometry, " \
|
||||||
|
# "ListSys, MillDrills,\n" \
|
||||||
|
# "# MillSlots, Mirror, New, NewExcellon, NewGeometry, NewGerber, Nregions, " \
|
||||||
|
# "Offset, OpenExcellon, OpenGCode, OpenGerber, OpenProject,\n" \
|
||||||
|
# "# Options, Paint, Panelize, PlotAl, PlotObjects, SaveProject, " \
|
||||||
|
# "SaveSys, Scale, SetActive, SetSys, SetOrigin, Skew, SubtractPoly,\n" \
|
||||||
|
# "# SubtractRectangle, Version, WriteGCode\n"
|
||||||
|
|
||||||
|
new_source_file = '# %s\n' % _('CREATE A NEW FLATCAM TCL SCRIPT') + \
|
||||||
|
'# %s:\n' % _('TCL Tutorial is here') + \
|
||||||
|
'# https://www.tcl.tk/man/tcl8.5/tutorial/tcltutorial.html\n' + '\n\n' + \
|
||||||
|
'# %s:\n' % _("FlatCAM commands list")
|
||||||
|
new_source_file += '# %s\n\n' % _("Type >help< followed by Run Code for a list of FlatCAM Tcl Commands "
|
||||||
|
"(displayed in Tcl Shell).")
|
||||||
|
|
||||||
|
def initialize(obj, app):
|
||||||
|
obj.source_file = deepcopy(new_source_file)
|
||||||
|
|
||||||
|
outname = 'new_script'
|
||||||
|
self.new_object('script', outname, initialize, plot=False)
|
||||||
|
|
||||||
|
def new_document_object(self):
|
||||||
|
"""
|
||||||
|
Creates a new, blank Document object.
|
||||||
|
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
|
||||||
|
def initialize(obj, app):
|
||||||
|
obj.source_file = ""
|
||||||
|
|
||||||
|
self.new_object('document', 'new_document', initialize, plot=False)
|
||||||
|
|
||||||
|
def on_object_created(self, obj, plot, auto_select):
|
||||||
|
"""
|
||||||
|
Event callback for object creation.
|
||||||
|
It will add the new object to the collection. After that it will plot the object in a threaded way
|
||||||
|
|
||||||
|
:param obj: The newly created FlatCAM object.
|
||||||
|
:param plot: if the newly create object t obe plotted
|
||||||
|
:param auto_select: if the newly created object to be autoselected after creation
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
t0 = time.time() # DEBUG
|
||||||
|
log.debug("on_object_created()")
|
||||||
|
|
||||||
|
# The Collection might change the name if there is a collision
|
||||||
|
self.app.collection.append(obj)
|
||||||
|
|
||||||
|
# after adding the object to the collection always update the list of objects that are in the collection
|
||||||
|
self.app.all_objects_list = self.app.collection.get_list()
|
||||||
|
|
||||||
|
# self.app.inform.emit('[selected] %s created & selected: %s' %
|
||||||
|
# (str(obj.kind).capitalize(), str(obj.options['name'])))
|
||||||
|
if obj.kind == 'gerber':
|
||||||
|
self.app.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.app.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.app.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.app.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.app.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.app.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.app.shell._edit.set_model_data(self.app.myKeywords)
|
||||||
|
|
||||||
|
if auto_select:
|
||||||
|
# select the just opened object but deselect the previous ones
|
||||||
|
self.app.collection.set_all_inactive()
|
||||||
|
self.app.collection.set_active(obj.options["name"])
|
||||||
|
else:
|
||||||
|
self.app.collection.set_all_inactive()
|
||||||
|
|
||||||
|
# here it is done the object plotting
|
||||||
|
def task(t_obj):
|
||||||
|
with self.app.proc_container.new(_("Plotting")):
|
||||||
|
if t_obj.kind == 'cncjob':
|
||||||
|
t_obj.plot(kind=self.app.defaults["cncjob_plot_kind"])
|
||||||
|
else:
|
||||||
|
t_obj.plot()
|
||||||
|
|
||||||
|
t1 = time.time() # DEBUG
|
||||||
|
log.debug("%f seconds adding object and plotting." % (t1 - t0))
|
||||||
|
self.object_plotted.emit(t_obj)
|
||||||
|
|
||||||
|
# Send to worker
|
||||||
|
# self.worker.add_task(worker_task, [self])
|
||||||
|
if plot is True:
|
||||||
|
self.app.worker_task.emit({'fcn': task, 'params': [obj]})
|
||||||
|
|
||||||
|
def on_object_changed(self, obj):
|
||||||
|
"""
|
||||||
|
Called whenever the geometry of the object was changed in some way.
|
||||||
|
This require the update of it's bounding values so it can be the selected on canvas.
|
||||||
|
Update the bounding box data from obj.options
|
||||||
|
|
||||||
|
:param obj: the object that was changed
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
xmin, ymin, xmax, ymax = obj.bounds()
|
||||||
|
except TypeError:
|
||||||
|
return
|
||||||
|
obj.options['xmin'] = xmin
|
||||||
|
obj.options['ymin'] = ymin
|
||||||
|
obj.options['xmax'] = xmax
|
||||||
|
obj.options['ymax'] = ymax
|
||||||
|
|
||||||
|
log.debug("Object changed, updating the bounding box data on self.options")
|
||||||
|
# delete the old selection shape
|
||||||
|
self.app.delete_selection_shape()
|
||||||
|
self.app.should_we_save = True
|
||||||
|
|
||||||
|
def on_object_plotted(self):
|
||||||
|
"""
|
||||||
|
Callback called whenever the plotted object needs to be fit into the viewport (canvas)
|
||||||
|
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
self.app.on_zoom_fit()
|
||||||
@@ -14,8 +14,8 @@ from copy import deepcopy
|
|||||||
from io import StringIO
|
from io import StringIO
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from flatcamEditors.FlatCAMTextEditor import TextEditor
|
from AppEditors.FlatCAMTextEditor import TextEditor
|
||||||
from flatcamObjects.FlatCAMObj import *
|
from AppObjects.FlatCAMObj import *
|
||||||
|
|
||||||
from camlib import CNCjob
|
from camlib import CNCjob
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ import sys
|
|||||||
import math
|
import math
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -506,10 +506,10 @@ class CNCJobObject(FlatCAMObj, CNCjob):
|
|||||||
filename, _f = FCFileSaveDialog.get_saved_filename(
|
filename, _f = FCFileSaveDialog.get_saved_filename(
|
||||||
caption=_("Export Machine Code ..."),
|
caption=_("Export Machine Code ..."),
|
||||||
directory=dir_file_to_save,
|
directory=dir_file_to_save,
|
||||||
filter=_filter_
|
ext_filter=_filter_
|
||||||
)
|
)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export Machine Code ..."), filter=_filter_)
|
filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export Machine Code ..."), ext_filter=_filter_)
|
||||||
|
|
||||||
filename = str(filename)
|
filename = str(filename)
|
||||||
|
|
||||||
@@ -564,7 +564,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
|
|||||||
|
|
||||||
# delete the absolute and relative position and messages in the infobar
|
# delete the absolute and relative position and messages in the infobar
|
||||||
self.app.ui.position_label.setText("")
|
self.app.ui.position_label.setText("")
|
||||||
self.app.ui.rel_position_label.setText("")
|
# self.app.ui.rel_position_label.setText("")
|
||||||
|
|
||||||
# first clear previous text in text editor (if any)
|
# first clear previous text in text editor (if any)
|
||||||
self.gcode_editor_tab.code_editor.clear()
|
self.gcode_editor_tab.code_editor.clear()
|
||||||
@@ -989,7 +989,6 @@ class CNCJobObject(FlatCAMObj, CNCjob):
|
|||||||
for key in self.cnc_tools:
|
for key in self.cnc_tools:
|
||||||
ppg = self.cnc_tools[key]['data']['ppname_g']
|
ppg = self.cnc_tools[key]['data']['ppname_g']
|
||||||
if 'toolchange_custom' not in str(ppg).lower():
|
if 'toolchange_custom' not in str(ppg).lower():
|
||||||
print(ppg)
|
|
||||||
if self.ui.toolchange_cb.get_value():
|
if self.ui.toolchange_cb.get_value():
|
||||||
self.ui.toolchange_cb.set_value(False)
|
self.ui.toolchange_cb.set_value(False)
|
||||||
self.app.inform.emit('[WARNING_NOTCL] %s' %
|
self.app.inform.emit('[WARNING_NOTCL] %s' %
|
||||||
@@ -1107,7 +1106,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
# we may have a tuple with only one element and a comma
|
# we may have a tuple with only one element and a comma
|
||||||
dia_plot = [float(el) for el in self.options["tooldia"].split(',') if el != ''][0]
|
dia_plot = [float(el) for el in self.options["tooldia"].split(',') if el != ''][0]
|
||||||
self.plot2(dia_plot, obj=self, visible=visible, kind=kind)
|
self.plot2(tooldia=dia_plot, obj=self, visible=visible, kind=kind)
|
||||||
else:
|
else:
|
||||||
# multiple tools usage
|
# multiple tools usage
|
||||||
if self.cnc_tools:
|
if self.cnc_tools:
|
||||||
@@ -1117,10 +1116,14 @@ class CNCJobObject(FlatCAMObj, CNCjob):
|
|||||||
self.plot2(tooldia=tooldia, obj=self, visible=visible, gcode_parsed=gcode_parsed, kind=kind)
|
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
|
# TODO: until the gcode parsed will be stored on each Excellon tool this will not get executed
|
||||||
|
# I do this so the travel lines thickness will reflect the tool diameter
|
||||||
|
# may work only for objects created within the app and not Gcode imported from elsewhere for which we
|
||||||
|
# don't know the origin
|
||||||
|
if self.origin_kind == "excellon":
|
||||||
if self.exc_cnc_tools:
|
if self.exc_cnc_tools:
|
||||||
for tooldia_key in self.exc_cnc_tools:
|
for tooldia_key in self.exc_cnc_tools:
|
||||||
tooldia = float('%.*f' % (self.decimals, float(tooldia_key)))
|
tooldia = float('%.*f' % (self.decimals, float(tooldia_key)))
|
||||||
# gcode_parsed = self.cnc_tools[tooldia_key]['gcode_parsed']
|
# gcode_parsed = self.exc_cnc_tools[tooldia_key]['gcode_parsed']
|
||||||
gcode_parsed = self.gcode_parsed
|
gcode_parsed = self.gcode_parsed
|
||||||
self.plot2(tooldia=tooldia, obj=self, visible=visible, gcode_parsed=gcode_parsed, kind=kind)
|
self.plot2(tooldia=tooldia, obj=self, visible=visible, gcode_parsed=gcode_parsed, kind=kind)
|
||||||
|
|
||||||
@@ -10,11 +10,11 @@
|
|||||||
# File modified by: Marius Stanciu #
|
# File modified by: Marius Stanciu #
|
||||||
# ##########################################################
|
# ##########################################################
|
||||||
|
|
||||||
from flatcamEditors.FlatCAMTextEditor import TextEditor
|
from AppEditors.FlatCAMTextEditor import TextEditor
|
||||||
from flatcamObjects.FlatCAMObj import *
|
from AppObjects.FlatCAMObj import *
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -284,6 +284,27 @@ class DocumentObject(FlatCAMObj):
|
|||||||
self.ui.sel_color_entry.set_value(new_val)
|
self.ui.sel_color_entry.set_value(new_val)
|
||||||
self.app.defaults['document_sel_color'] = new_val
|
self.app.defaults['document_sel_color'] = new_val
|
||||||
|
|
||||||
|
def mirror(self, axis, point):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def offset(self, vect):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def rotate(self, angle, point):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def scale(self, xfactor, yfactor=None, point=None):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def skew(self, angle_x, angle_y, point):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def buffer(self, distance, join, factor=None):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def bounds(self, flatten=False):
|
||||||
|
return None, None, None, None
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
"""
|
"""
|
||||||
Returns a representation of the object as a dictionary.
|
Returns a representation of the object as a dictionary.
|
||||||
@@ -15,13 +15,14 @@ from shapely.geometry import Point, LineString
|
|||||||
|
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
from flatcamParsers.ParseExcellon import Excellon
|
from AppParsers.ParseExcellon import Excellon
|
||||||
from flatcamObjects.FlatCAMObj import *
|
from AppObjects.FlatCAMObj import *
|
||||||
|
|
||||||
import itertools
|
import itertools
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -31,7 +32,7 @@ if '_' not in builtins.__dict__:
|
|||||||
|
|
||||||
class ExcellonObject(FlatCAMObj, Excellon):
|
class ExcellonObject(FlatCAMObj, Excellon):
|
||||||
"""
|
"""
|
||||||
Represents Excellon/Drill code.
|
Represents Excellon/Drill code. An object stored in the FlatCAM objects collection (a dict)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ui_type = ExcellonObjectUI
|
ui_type = ExcellonObjectUI
|
||||||
@@ -50,6 +51,7 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
self.options.update({
|
self.options.update({
|
||||||
"plot": True,
|
"plot": True,
|
||||||
"solid": False,
|
"solid": False,
|
||||||
|
"multicolored": False,
|
||||||
|
|
||||||
"operation": "drill",
|
"operation": "drill",
|
||||||
"milling_type": "drills",
|
"milling_type": "drills",
|
||||||
@@ -125,6 +127,10 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
self.outline_color = self.app.defaults['excellon_plot_line']
|
self.outline_color = self.app.defaults['excellon_plot_line']
|
||||||
self.alpha_level = 'bf'
|
self.alpha_level = 'bf'
|
||||||
|
|
||||||
|
# store here the state of the exclusion checkbox state to be restored after building the UI
|
||||||
|
# TODO add this in the sel.app.defaults dict and in Preferences
|
||||||
|
self.exclusion_area_cb_is_checked = False
|
||||||
|
|
||||||
# Attributes to be included in serialization
|
# Attributes to be included in serialization
|
||||||
# Always append to it because it carries contents
|
# Always append to it because it carries contents
|
||||||
# from predecessors.
|
# from predecessors.
|
||||||
@@ -143,7 +149,9 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
If only one object is in exc_list parameter then this function will copy that object in the exc_final
|
If only one object is in exc_list parameter then this function will copy that object in the exc_final
|
||||||
|
|
||||||
:param exc_list: List or one object of ExcellonObject Objects to join.
|
:param exc_list: List or one object of ExcellonObject Objects to join.
|
||||||
|
:type exc_list: list
|
||||||
:param exc_final: Destination ExcellonObject object.
|
:param exc_final: Destination ExcellonObject object.
|
||||||
|
:type exc_final: class
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -312,8 +320,23 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
exc_final.create_geometry()
|
exc_final.create_geometry()
|
||||||
|
|
||||||
def build_ui(self):
|
def build_ui(self):
|
||||||
|
"""
|
||||||
|
Will (re)build the Excellon UI updating it (the tool table)
|
||||||
|
|
||||||
|
:return: None
|
||||||
|
:rtype:
|
||||||
|
"""
|
||||||
FlatCAMObj.build_ui(self)
|
FlatCAMObj.build_ui(self)
|
||||||
|
|
||||||
|
# Area Exception - exclusion shape added signal
|
||||||
|
# first disconnect it from any other object
|
||||||
|
try:
|
||||||
|
self.app.exc_areas.e_shape_modified.disconnect()
|
||||||
|
except (TypeError, AttributeError):
|
||||||
|
pass
|
||||||
|
# then connect it to the current build_ui() method
|
||||||
|
self.app.exc_areas.e_shape_modified.connect(self.update_exclusion_table)
|
||||||
|
|
||||||
self.units = self.app.defaults['units'].upper()
|
self.units = self.app.defaults['units'].upper()
|
||||||
|
|
||||||
for row in range(self.ui.tools_table.rowCount()):
|
for row in range(self.ui.tools_table.rowCount()):
|
||||||
@@ -514,6 +537,58 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
"<b>%s: <font color='#0000FF'>%s</font></b>" % (_('Parameters for'), _("Multiple Tools"))
|
"<b>%s: <font color='#0000FF'>%s</font></b>" % (_('Parameters for'), _("Multiple Tools"))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Build Exclusion Areas section
|
||||||
|
e_len = len(self.app.exc_areas.exclusion_areas_storage)
|
||||||
|
self.ui.exclusion_table.setRowCount(e_len)
|
||||||
|
|
||||||
|
area_id = 0
|
||||||
|
|
||||||
|
for area in range(e_len):
|
||||||
|
area_id += 1
|
||||||
|
|
||||||
|
area_dict = self.app.exc_areas.exclusion_areas_storage[area]
|
||||||
|
|
||||||
|
area_id_item = QtWidgets.QTableWidgetItem('%d' % int(area_id))
|
||||||
|
area_id_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||||
|
self.ui.exclusion_table.setItem(area, 0, area_id_item) # Area id
|
||||||
|
|
||||||
|
object_item = QtWidgets.QTableWidgetItem('%s' % area_dict["obj_type"])
|
||||||
|
object_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||||
|
self.ui.exclusion_table.setItem(area, 1, object_item) # Origin Object
|
||||||
|
|
||||||
|
strategy_item = QtWidgets.QTableWidgetItem('%s' % area_dict["strategy"])
|
||||||
|
strategy_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||||
|
self.ui.exclusion_table.setItem(area, 2, strategy_item) # Strategy
|
||||||
|
|
||||||
|
overz_item = QtWidgets.QTableWidgetItem('%s' % area_dict["overz"])
|
||||||
|
overz_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||||
|
self.ui.exclusion_table.setItem(area, 3, overz_item) # Over Z
|
||||||
|
|
||||||
|
self.ui.exclusion_table.resizeColumnsToContents()
|
||||||
|
self.ui.exclusion_table.resizeRowsToContents()
|
||||||
|
|
||||||
|
area_vheader = self.ui.exclusion_table.verticalHeader()
|
||||||
|
area_vheader.hide()
|
||||||
|
self.ui.exclusion_table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||||
|
|
||||||
|
area_hheader = self.ui.exclusion_table.horizontalHeader()
|
||||||
|
area_hheader.setMinimumSectionSize(10)
|
||||||
|
area_hheader.setDefaultSectionSize(70)
|
||||||
|
|
||||||
|
area_hheader.setSectionResizeMode(0, QtWidgets.QHeaderView.Fixed)
|
||||||
|
area_hheader.resizeSection(0, 20)
|
||||||
|
area_hheader.setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch)
|
||||||
|
area_hheader.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents)
|
||||||
|
area_hheader.setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeToContents)
|
||||||
|
|
||||||
|
# area_hheader.setStretchLastSection(True)
|
||||||
|
self.ui.exclusion_table.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||||
|
|
||||||
|
self.ui.exclusion_table.setColumnWidth(0, 20)
|
||||||
|
|
||||||
|
self.ui.exclusion_table.setMinimumHeight(self.ui.exclusion_table.getHeight())
|
||||||
|
self.ui.exclusion_table.setMaximumHeight(self.ui.exclusion_table.getHeight())
|
||||||
|
|
||||||
self.ui_connect()
|
self.ui_connect()
|
||||||
|
|
||||||
def set_ui(self, ui):
|
def set_ui(self, ui):
|
||||||
@@ -534,6 +609,7 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
self.form_fields.update({
|
self.form_fields.update({
|
||||||
"plot": self.ui.plot_cb,
|
"plot": self.ui.plot_cb,
|
||||||
"solid": self.ui.solid_cb,
|
"solid": self.ui.solid_cb,
|
||||||
|
"multicolored": self.ui.multicolored_cb,
|
||||||
|
|
||||||
"operation": self.ui.operation_radio,
|
"operation": self.ui.operation_radio,
|
||||||
"milling_type": self.ui.milling_type_radio,
|
"milling_type": self.ui.milling_type_radio,
|
||||||
@@ -567,7 +643,11 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
"ppname_g": self.ui.pp_geo_name_cb,
|
"ppname_g": self.ui.pp_geo_name_cb,
|
||||||
"z_pdepth": self.ui.pdepth_entry,
|
"z_pdepth": self.ui.pdepth_entry,
|
||||||
"feedrate_probe": self.ui.feedrate_probe_entry,
|
"feedrate_probe": self.ui.feedrate_probe_entry,
|
||||||
# "gcode_type": self.ui.excellon_gcode_type_radio
|
# "gcode_type": self.ui.excellon_gcode_type_radio,
|
||||||
|
"area_exclusion": self.ui.exclusion_cb,
|
||||||
|
"area_shape": self.ui.area_shape_radio,
|
||||||
|
"area_strategy": self.ui.strategy_radio,
|
||||||
|
"area_overz": self.ui.over_z_entry,
|
||||||
})
|
})
|
||||||
|
|
||||||
self.name2option = {
|
self.name2option = {
|
||||||
@@ -628,12 +708,24 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
|
|
||||||
assert isinstance(self.ui, ExcellonObjectUI), \
|
assert isinstance(self.ui, ExcellonObjectUI), \
|
||||||
"Expected a ExcellonObjectUI, got %s" % type(self.ui)
|
"Expected a ExcellonObjectUI, got %s" % type(self.ui)
|
||||||
|
|
||||||
self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click)
|
self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click)
|
||||||
self.ui.solid_cb.stateChanged.connect(self.on_solid_cb_click)
|
self.ui.solid_cb.stateChanged.connect(self.on_solid_cb_click)
|
||||||
|
self.ui.multicolored_cb.stateChanged.connect(self.on_multicolored_cb_click)
|
||||||
|
|
||||||
self.ui.generate_cnc_button.clicked.connect(self.on_create_cncjob_button_click)
|
self.ui.generate_cnc_button.clicked.connect(self.on_create_cncjob_button_click)
|
||||||
self.ui.generate_milling_button.clicked.connect(self.on_generate_milling_button_click)
|
self.ui.generate_milling_button.clicked.connect(self.on_generate_milling_button_click)
|
||||||
self.ui.generate_milling_slots_button.clicked.connect(self.on_generate_milling_slots_button_click)
|
self.ui.generate_milling_slots_button.clicked.connect(self.on_generate_milling_slots_button_click)
|
||||||
|
|
||||||
|
# Exclusion areas signals
|
||||||
|
self.ui.exclusion_table.horizontalHeader().sectionClicked.connect(self.exclusion_table_toggle_all)
|
||||||
|
self.ui.exclusion_table.lost_focus.connect(self.clear_selection)
|
||||||
|
self.ui.exclusion_table.itemClicked.connect(self.draw_sel_shape)
|
||||||
|
self.ui.add_area_button.clicked.connect(self.on_add_area_click)
|
||||||
|
self.ui.delete_area_button.clicked.connect(self.on_clear_area_click)
|
||||||
|
self.ui.delete_sel_area_button.clicked.connect(self.on_delete_sel_areas)
|
||||||
|
self.ui.strategy_radio.activated_custom.connect(self.on_strategy)
|
||||||
|
|
||||||
self.on_operation_type(val='drill')
|
self.on_operation_type(val='drill')
|
||||||
self.ui.operation_radio.activated_custom.connect(self.on_operation_type)
|
self.ui.operation_radio.activated_custom.connect(self.on_operation_type)
|
||||||
|
|
||||||
@@ -650,6 +742,12 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
self.ui.operation_radio.setEnabled(False)
|
self.ui.operation_radio.setEnabled(False)
|
||||||
|
|
||||||
def ui_connect(self):
|
def ui_connect(self):
|
||||||
|
"""
|
||||||
|
Will connect all signals in the Excellon UI that needs to be connected
|
||||||
|
|
||||||
|
:return: None
|
||||||
|
:rtype:
|
||||||
|
"""
|
||||||
|
|
||||||
# selective plotting
|
# selective plotting
|
||||||
for row in range(self.ui.tools_table.rowCount() - 2):
|
for row in range(self.ui.tools_table.rowCount() - 2):
|
||||||
@@ -672,6 +770,12 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
current_widget.returnPressed.connect(self.form_to_storage)
|
current_widget.returnPressed.connect(self.form_to_storage)
|
||||||
|
|
||||||
def ui_disconnect(self):
|
def ui_disconnect(self):
|
||||||
|
"""
|
||||||
|
Will disconnect all signals in the Excellon UI that needs to be disconnected
|
||||||
|
|
||||||
|
:return: None
|
||||||
|
:rtype:
|
||||||
|
"""
|
||||||
# selective plotting
|
# selective plotting
|
||||||
for row in range(self.ui.tools_table.rowCount()):
|
for row in range(self.ui.tools_table.rowCount()):
|
||||||
try:
|
try:
|
||||||
@@ -714,6 +818,12 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def on_row_selection_change(self):
|
def on_row_selection_change(self):
|
||||||
|
"""
|
||||||
|
Called when the user clicks on a row in Tools Table
|
||||||
|
|
||||||
|
:return: None
|
||||||
|
:rtype:
|
||||||
|
"""
|
||||||
self.ui_disconnect()
|
self.ui_disconnect()
|
||||||
|
|
||||||
sel_rows = []
|
sel_rows = []
|
||||||
@@ -764,6 +874,14 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
self.ui_connect()
|
self.ui_connect()
|
||||||
|
|
||||||
def storage_to_form(self, dict_storage):
|
def storage_to_form(self, dict_storage):
|
||||||
|
"""
|
||||||
|
Will update the GUI with data from the "storage" in this case the dict self.tools
|
||||||
|
|
||||||
|
:param dict_storage: A dictionary holding the data relevant for gnerating Gcode from Excellon
|
||||||
|
:type dict_storage: dict
|
||||||
|
:return: None
|
||||||
|
:rtype:
|
||||||
|
"""
|
||||||
for form_key in self.form_fields:
|
for form_key in self.form_fields:
|
||||||
for storage_key in dict_storage:
|
for storage_key in dict_storage:
|
||||||
if form_key == storage_key and form_key not in \
|
if form_key == storage_key and form_key not in \
|
||||||
@@ -775,6 +893,12 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def form_to_storage(self):
|
def form_to_storage(self):
|
||||||
|
"""
|
||||||
|
Will update the 'storage' attribute which is the dict self.tools with data collected from GUI
|
||||||
|
|
||||||
|
:return: None
|
||||||
|
:rtype:
|
||||||
|
"""
|
||||||
if self.ui.tools_table.rowCount() == 0:
|
if self.ui.tools_table.rowCount() == 0:
|
||||||
# there is no tool in tool table so we can't save the GUI elements values to storage
|
# there is no tool in tool table so we can't save the GUI elements values to storage
|
||||||
return
|
return
|
||||||
@@ -803,6 +927,14 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
self.ui_connect()
|
self.ui_connect()
|
||||||
|
|
||||||
def on_operation_type(self, val):
|
def on_operation_type(self, val):
|
||||||
|
"""
|
||||||
|
Called by a RadioSet activated_custom signal
|
||||||
|
|
||||||
|
:param val: Parameter passes by the signal that called this method
|
||||||
|
:type val: str
|
||||||
|
:return: None
|
||||||
|
:rtype:
|
||||||
|
"""
|
||||||
if val == 'mill':
|
if val == 'mill':
|
||||||
self.ui.mill_type_label.show()
|
self.ui.mill_type_label.show()
|
||||||
self.ui.milling_type_radio.show()
|
self.ui.milling_type_radio.show()
|
||||||
@@ -831,7 +963,7 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
def get_selected_tools_list(self):
|
def get_selected_tools_list(self):
|
||||||
"""
|
"""
|
||||||
Returns the keys to the self.tools dictionary corresponding
|
Returns the keys to the self.tools dictionary corresponding
|
||||||
to the selections on the tool list in the GUI.
|
to the selections on the tool list in the AppGUI.
|
||||||
|
|
||||||
:return: List of tools.
|
:return: List of tools.
|
||||||
:rtype: list
|
:rtype: list
|
||||||
@@ -872,7 +1004,21 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
def export_excellon(self, whole, fract, e_zeros=None, form='dec', factor=1, slot_type='routing'):
|
def export_excellon(self, whole, fract, e_zeros=None, form='dec', factor=1, slot_type='routing'):
|
||||||
"""
|
"""
|
||||||
Returns two values, first is a boolean , if 1 then the file has slots and second contain the Excellon code
|
Returns two values, first is a boolean , if 1 then the file has slots and second contain the Excellon code
|
||||||
:return: has_slots and Excellon_code
|
|
||||||
|
:param whole: Integer part digits
|
||||||
|
:type whole: int
|
||||||
|
:param fract: Fractional part digits
|
||||||
|
:type fract: int
|
||||||
|
:param e_zeros: Excellon zeros suppression: LZ or TZ
|
||||||
|
:type e_zeros: str
|
||||||
|
:param form: Excellon format: 'dec',
|
||||||
|
:type form: str
|
||||||
|
:param factor: Conversion factor
|
||||||
|
:type factor: float
|
||||||
|
:param slot_type: How to treat slots: "routing" or "drilling"
|
||||||
|
:type slot_type: str
|
||||||
|
:return: A tuple: (has_slots, Excellon_code) -> (bool, str)
|
||||||
|
:rtype: tuple
|
||||||
"""
|
"""
|
||||||
|
|
||||||
excellon_code = ''
|
excellon_code = ''
|
||||||
@@ -1039,11 +1185,23 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
|
|
||||||
def generate_milling_drills(self, tools=None, outname=None, tooldia=None, plot=False, use_thread=False):
|
def generate_milling_drills(self, tools=None, outname=None, tooldia=None, plot=False, use_thread=False):
|
||||||
"""
|
"""
|
||||||
|
Will generate an Geometry Object allowing to cut a drill hole instead of drilling it.
|
||||||
|
|
||||||
Note: This method is a good template for generic operations as
|
Note: This method is a good template for generic operations as
|
||||||
it takes it's options from parameters or otherwise from the
|
it takes it's options from parameters or otherwise from the
|
||||||
object's options and returns a (success, msg) tuple as feedback
|
object's options and returns a (success, msg) tuple as feedback
|
||||||
for shell operations.
|
for shell operations.
|
||||||
|
|
||||||
|
:param tools: A list of tools where the drills are to be milled or a string: "all"
|
||||||
|
:type tools:
|
||||||
|
:param outname: the name of the resulting Geometry object
|
||||||
|
:type outname: str
|
||||||
|
:param tooldia: the tool diameter to be used in creation of the milling path (Geometry Object)
|
||||||
|
:type tooldia: float
|
||||||
|
:param plot: if to plot the resulting object
|
||||||
|
:type plot: bool
|
||||||
|
:param use_thread: if to use threading for creation of the Geometry object
|
||||||
|
:type use_thread: bool
|
||||||
:return: Success/failure condition tuple (bool, str).
|
:return: Success/failure condition tuple (bool, str).
|
||||||
:rtype: tuple
|
:rtype: tuple
|
||||||
"""
|
"""
|
||||||
@@ -1088,6 +1246,15 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
return False, "Error: Milling tool is larger than hole."
|
return False, "Error: Milling tool is larger than hole."
|
||||||
|
|
||||||
def geo_init(geo_obj, app_obj):
|
def geo_init(geo_obj, app_obj):
|
||||||
|
"""
|
||||||
|
|
||||||
|
:param geo_obj: New object
|
||||||
|
:type geo_obj: GeometryObject
|
||||||
|
:param app_obj: App
|
||||||
|
:type app_obj: FlatCAMApp.App
|
||||||
|
:return:
|
||||||
|
:rtype:
|
||||||
|
"""
|
||||||
assert geo_obj.kind == 'geometry', "Initializer expected a GeometryObject, got %s" % type(geo_obj)
|
assert geo_obj.kind == 'geometry', "Initializer expected a GeometryObject, got %s" % type(geo_obj)
|
||||||
|
|
||||||
# ## Add properties to the object
|
# ## Add properties to the object
|
||||||
@@ -1100,7 +1267,7 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
geo_obj.options['Tools_in_use'] = tool_table_items
|
geo_obj.options['Tools_in_use'] = tool_table_items
|
||||||
geo_obj.options['type'] = 'Excellon Geometry'
|
geo_obj.options['type'] = 'Excellon Geometry'
|
||||||
geo_obj.options["cnctooldia"] = str(tooldia)
|
geo_obj.options["cnctooldia"] = str(tooldia)
|
||||||
|
geo_obj.options["multidepth"] = self.options["multidepth"]
|
||||||
geo_obj.solid_geometry = []
|
geo_obj.solid_geometry = []
|
||||||
|
|
||||||
# in case that the tool used has the same diameter with the hole, and since the maximum resolution
|
# in case that the tool used has the same diameter with the hole, and since the maximum resolution
|
||||||
@@ -1115,9 +1282,10 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
else:
|
else:
|
||||||
geo_obj.solid_geometry.append(
|
geo_obj.solid_geometry.append(
|
||||||
Point(hole['point']).buffer(buffer_value).exterior)
|
Point(hole['point']).buffer(buffer_value).exterior)
|
||||||
|
|
||||||
if use_thread:
|
if use_thread:
|
||||||
def geo_thread(app_obj):
|
def geo_thread(a_obj):
|
||||||
app_obj.new_object("geometry", outname, geo_init, plot=plot)
|
a_obj.app_obj.new_object("geometry", outname, geo_init, plot=plot)
|
||||||
|
|
||||||
# Create a promise with the new name
|
# Create a promise with the new name
|
||||||
self.app.collection.promise(outname)
|
self.app.collection.promise(outname)
|
||||||
@@ -1125,17 +1293,29 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
# Send to worker
|
# Send to worker
|
||||||
self.app.worker_task.emit({'fcn': geo_thread, 'params': [self.app]})
|
self.app.worker_task.emit({'fcn': geo_thread, 'params': [self.app]})
|
||||||
else:
|
else:
|
||||||
self.app.new_object("geometry", outname, geo_init, plot=plot)
|
self.app.app_obj.new_object("geometry", outname, geo_init, plot=plot)
|
||||||
|
|
||||||
return True, ""
|
return True, ""
|
||||||
|
|
||||||
def generate_milling_slots(self, tools=None, outname=None, tooldia=None, plot=True, use_thread=False):
|
def generate_milling_slots(self, tools=None, outname=None, tooldia=None, plot=False, use_thread=False):
|
||||||
"""
|
"""
|
||||||
|
Will generate an Geometry Object allowing to cut/mill a slot hole.
|
||||||
|
|
||||||
Note: This method is a good template for generic operations as
|
Note: This method is a good template for generic operations as
|
||||||
it takes it's options from parameters or otherwise from the
|
it takes it's options from parameters or otherwise from the
|
||||||
object's options and returns a (success, msg) tuple as feedback
|
object's options and returns a (success, msg) tuple as feedback
|
||||||
for shell operations.
|
for shell operations.
|
||||||
|
|
||||||
|
:param tools: A list of tools where the drills are to be milled or a string: "all"
|
||||||
|
:type tools:
|
||||||
|
:param outname: the name of the resulting Geometry object
|
||||||
|
:type outname: str
|
||||||
|
:param tooldia: the tool diameter to be used in creation of the milling path (Geometry Object)
|
||||||
|
:type tooldia: float
|
||||||
|
:param plot: if to plot the resulting object
|
||||||
|
:type plot: bool
|
||||||
|
:param use_thread: if to use threading for creation of the Geometry object
|
||||||
|
:type use_thread: bool
|
||||||
:return: Success/failure condition tuple (bool, str).
|
:return: Success/failure condition tuple (bool, str).
|
||||||
:rtype: tuple
|
:rtype: tuple
|
||||||
"""
|
"""
|
||||||
@@ -1178,7 +1358,7 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
return False, "Error: Milling tool is larger than hole."
|
return False, "Error: Milling tool is larger than hole."
|
||||||
|
|
||||||
def geo_init(geo_obj, app_obj):
|
def geo_init(geo_obj, app_obj):
|
||||||
assert geo_obj.kind == 'geometry' "Initializer expected a GeometryObject, got %s" % type(geo_obj)
|
assert geo_obj.kind == 'geometry', "Initializer expected a GeometryObject, got %s" % type(geo_obj)
|
||||||
|
|
||||||
# ## Add properties to the object
|
# ## Add properties to the object
|
||||||
|
|
||||||
@@ -1190,7 +1370,7 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
geo_obj.options['Tools_in_use'] = tool_table_items
|
geo_obj.options['Tools_in_use'] = tool_table_items
|
||||||
geo_obj.options['type'] = 'Excellon Geometry'
|
geo_obj.options['type'] = 'Excellon Geometry'
|
||||||
geo_obj.options["cnctooldia"] = str(tooldia)
|
geo_obj.options["cnctooldia"] = str(tooldia)
|
||||||
|
geo_obj.options["multidepth"] = self.options["multidepth"]
|
||||||
geo_obj.solid_geometry = []
|
geo_obj.solid_geometry = []
|
||||||
|
|
||||||
# in case that the tool used has the same diameter with the hole, and since the maximum resolution
|
# in case that the tool used has the same diameter with the hole, and since the maximum resolution
|
||||||
@@ -1220,8 +1400,8 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
geo_obj.solid_geometry.append(poly)
|
geo_obj.solid_geometry.append(poly)
|
||||||
|
|
||||||
if use_thread:
|
if use_thread:
|
||||||
def geo_thread(app_obj):
|
def geo_thread(a_obj):
|
||||||
app_obj.new_object("geometry", outname + '_slot', geo_init, plot=plot)
|
a_obj.app_obj.new_object("geometry", outname + '_slot', geo_init, plot=plot)
|
||||||
|
|
||||||
# Create a promise with the new name
|
# Create a promise with the new name
|
||||||
self.app.collection.promise(outname)
|
self.app.collection.promise(outname)
|
||||||
@@ -1229,7 +1409,7 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
# Send to worker
|
# Send to worker
|
||||||
self.app.worker_task.emit({'fcn': geo_thread, 'params': [self.app]})
|
self.app.worker_task.emit({'fcn': geo_thread, 'params': [self.app]})
|
||||||
else:
|
else:
|
||||||
self.app.new_object("geometry", outname + '_slot', geo_init, plot=plot)
|
self.app.app_obj.new_object("geometry", outname + '_slot', geo_init, plot=plot)
|
||||||
|
|
||||||
return True, ""
|
return True, ""
|
||||||
|
|
||||||
@@ -1237,13 +1417,13 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
self.app.defaults.report_usage("excellon_on_create_milling_drills button")
|
self.app.defaults.report_usage("excellon_on_create_milling_drills button")
|
||||||
self.read_form()
|
self.read_form()
|
||||||
|
|
||||||
self.generate_milling_drills(use_thread=False)
|
self.generate_milling_drills(use_thread=False, plot=True)
|
||||||
|
|
||||||
def on_generate_milling_slots_button_click(self, *args):
|
def on_generate_milling_slots_button_click(self, *args):
|
||||||
self.app.defaults.report_usage("excellon_on_create_milling_slots_button")
|
self.app.defaults.report_usage("excellon_on_create_milling_slots_button")
|
||||||
self.read_form()
|
self.read_form()
|
||||||
|
|
||||||
self.generate_milling_slots(use_thread=False)
|
self.generate_milling_slots(use_thread=False, plot=True)
|
||||||
|
|
||||||
def on_pp_changed(self):
|
def on_pp_changed(self):
|
||||||
current_pp = self.ui.pp_excellon_name_cb.get_value()
|
current_pp = self.ui.pp_excellon_name_cb.get_value()
|
||||||
@@ -1363,7 +1543,7 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
job_name = self.options["name"] + "_cnc"
|
job_name = self.options["name"] + "_cnc"
|
||||||
pp_excellon_name = self.options["ppname_e"]
|
pp_excellon_name = self.options["ppname_e"]
|
||||||
|
|
||||||
# Object initialization function for app.new_object()
|
# Object initialization function for app.app_obj.new_object()
|
||||||
def job_init(job_obj, app_obj):
|
def job_init(job_obj, app_obj):
|
||||||
assert job_obj.kind == 'cncjob', "Initializer expected a CNCJobObject, got %s" % type(job_obj)
|
assert job_obj.kind == 'cncjob', "Initializer expected a CNCJobObject, got %s" % type(job_obj)
|
||||||
|
|
||||||
@@ -1426,9 +1606,9 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
job_obj.create_geometry()
|
job_obj.create_geometry()
|
||||||
|
|
||||||
# To be run in separate thread
|
# To be run in separate thread
|
||||||
def job_thread(app_obj):
|
def job_thread(a_obj):
|
||||||
with self.app.proc_container.new(_("Generating CNC Code")):
|
with self.app.proc_container.new(_("Generating CNC Code")):
|
||||||
app_obj.new_object("cncjob", job_name, job_init)
|
a_obj.app_obj.new_object("cncjob", job_name, job_init)
|
||||||
|
|
||||||
# Create promise for the new name.
|
# Create promise for the new name.
|
||||||
self.app.collection.promise(job_name)
|
self.app.collection.promise(job_name)
|
||||||
@@ -1466,12 +1646,122 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
# self.options['startz'] = float(self.options['startz']) * factor
|
# self.options['startz'] = float(self.options['startz']) * factor
|
||||||
# self.options['endz'] = float(self.options['endz']) * factor
|
# self.options['endz'] = float(self.options['endz']) * factor
|
||||||
|
|
||||||
|
def on_add_area_click(self):
|
||||||
|
shape_button = self.ui.area_shape_radio
|
||||||
|
overz_button = self.ui.over_z_entry
|
||||||
|
strategy_radio = self.ui.strategy_radio
|
||||||
|
cnc_button = self.ui.generate_cnc_button
|
||||||
|
solid_geo = self.solid_geometry
|
||||||
|
obj_type = self.kind
|
||||||
|
|
||||||
|
self.app.exc_areas.on_add_area_click(
|
||||||
|
shape_button=shape_button, overz_button=overz_button, cnc_button=cnc_button, strategy_radio=strategy_radio,
|
||||||
|
solid_geo=solid_geo, obj_type=obj_type)
|
||||||
|
|
||||||
|
def on_clear_area_click(self):
|
||||||
|
if not self.app.exc_areas.exclusion_areas_storage:
|
||||||
|
self.app.inform.emit("[WARNING_NOTCL] %s" % _("Delete failed. There are no exclusion areas to delete."))
|
||||||
|
return
|
||||||
|
|
||||||
|
self.app.exc_areas.on_clear_area_click()
|
||||||
|
self.app.exc_areas.e_shape_modified.emit()
|
||||||
|
|
||||||
|
def on_delete_sel_areas(self):
|
||||||
|
sel_model = self.ui.exclusion_table.selectionModel()
|
||||||
|
sel_indexes = sel_model.selectedIndexes()
|
||||||
|
|
||||||
|
# it will iterate over all indexes which means all items in all columns too but I'm interested only on rows
|
||||||
|
# so the duplicate rows will not be added
|
||||||
|
sel_rows = set()
|
||||||
|
for idx in sel_indexes:
|
||||||
|
sel_rows.add(idx.row())
|
||||||
|
|
||||||
|
if not sel_rows:
|
||||||
|
self.app.inform.emit("[WARNING_NOTCL] %s" % _("Delete failed. Nothing is selected."))
|
||||||
|
return
|
||||||
|
|
||||||
|
self.app.exc_areas.delete_sel_shapes(idxs=list(sel_rows))
|
||||||
|
self.app.exc_areas.e_shape_modified.emit()
|
||||||
|
|
||||||
|
def draw_sel_shape(self):
|
||||||
|
sel_model = self.ui.exclusion_table.selectionModel()
|
||||||
|
sel_indexes = sel_model.selectedIndexes()
|
||||||
|
|
||||||
|
# it will iterate over all indexes which means all items in all columns too but I'm interested only on rows
|
||||||
|
sel_rows = set()
|
||||||
|
for idx in sel_indexes:
|
||||||
|
sel_rows.add(idx.row())
|
||||||
|
|
||||||
|
self.delete_sel_shape()
|
||||||
|
|
||||||
|
if self.app.is_legacy is False:
|
||||||
|
face = self.app.defaults['global_sel_fill'][:-2] + str(hex(int(0.2 * 255)))[2:]
|
||||||
|
outline = self.app.defaults['global_sel_line'][:-2] + str(hex(int(0.8 * 255)))[2:]
|
||||||
|
else:
|
||||||
|
face = self.app.defaults['global_sel_fill'][:-2] + str(hex(int(0.4 * 255)))[2:]
|
||||||
|
outline = self.app.defaults['global_sel_line'][:-2] + str(hex(int(1.0 * 255)))[2:]
|
||||||
|
|
||||||
|
for row in sel_rows:
|
||||||
|
sel_rect = self.app.exc_areas.exclusion_areas_storage[row]['shape']
|
||||||
|
self.app.move_tool.sel_shapes.add(sel_rect, color=outline, face_color=face, update=True, layer=0,
|
||||||
|
tolerance=None)
|
||||||
|
if self.app.is_legacy is True:
|
||||||
|
self.app.move_tool.sel_shapes.redraw()
|
||||||
|
|
||||||
|
def clear_selection(self):
|
||||||
|
self.app.delete_selection_shape()
|
||||||
|
# self.ui.exclusion_table.clearSelection()
|
||||||
|
|
||||||
|
def delete_sel_shape(self):
|
||||||
|
self.app.delete_selection_shape()
|
||||||
|
|
||||||
|
def update_exclusion_table(self):
|
||||||
|
self.exclusion_area_cb_is_checked = True if self.ui.exclusion_cb.isChecked() else False
|
||||||
|
|
||||||
|
self.build_ui()
|
||||||
|
self.ui.exclusion_cb.set_value(self.exclusion_area_cb_is_checked)
|
||||||
|
|
||||||
|
def on_strategy(self, val):
|
||||||
|
if val == 'around':
|
||||||
|
self.ui.over_z_label.setDisabled(True)
|
||||||
|
self.ui.over_z_entry.setDisabled(True)
|
||||||
|
else:
|
||||||
|
self.ui.over_z_label.setDisabled(False)
|
||||||
|
self.ui.over_z_entry.setDisabled(False)
|
||||||
|
|
||||||
|
def exclusion_table_toggle_all(self):
|
||||||
|
"""
|
||||||
|
will toggle the selection of all rows in Exclusion Areas table
|
||||||
|
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
sel_model = self.ui.exclusion_table.selectionModel()
|
||||||
|
sel_indexes = sel_model.selectedIndexes()
|
||||||
|
|
||||||
|
# it will iterate over all indexes which means all items in all columns too but I'm interested only on rows
|
||||||
|
sel_rows = set()
|
||||||
|
for idx in sel_indexes:
|
||||||
|
sel_rows.add(idx.row())
|
||||||
|
|
||||||
|
if sel_rows:
|
||||||
|
self.ui.exclusion_table.clearSelection()
|
||||||
|
self.delete_sel_shape()
|
||||||
|
else:
|
||||||
|
self.ui.exclusion_table.selectAll()
|
||||||
|
self.draw_sel_shape()
|
||||||
|
|
||||||
def on_solid_cb_click(self, *args):
|
def on_solid_cb_click(self, *args):
|
||||||
if self.muted_ui:
|
if self.muted_ui:
|
||||||
return
|
return
|
||||||
self.read_form_item('solid')
|
self.read_form_item('solid')
|
||||||
self.plot()
|
self.plot()
|
||||||
|
|
||||||
|
def on_multicolored_cb_click(self, *args):
|
||||||
|
if self.muted_ui:
|
||||||
|
return
|
||||||
|
self.read_form_item('multicolored')
|
||||||
|
self.plot()
|
||||||
|
|
||||||
def on_plot_cb_click(self, *args):
|
def on_plot_cb_click(self, *args):
|
||||||
if self.muted_ui:
|
if self.muted_ui:
|
||||||
return
|
return
|
||||||
@@ -1545,6 +1835,27 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
if not FlatCAMObj.plot(self):
|
if not FlatCAMObj.plot(self):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if self.app.is_legacy is False:
|
||||||
|
def random_color():
|
||||||
|
r_color = np.random.rand(4)
|
||||||
|
r_color[3] = 1
|
||||||
|
return r_color
|
||||||
|
else:
|
||||||
|
def random_color():
|
||||||
|
while True:
|
||||||
|
r_color = np.random.rand(4)
|
||||||
|
r_color[3] = 1
|
||||||
|
|
||||||
|
new_color = '#'
|
||||||
|
for idx in range(len(r_color)):
|
||||||
|
new_color += '%x' % int(r_color[idx] * 255)
|
||||||
|
# do it until a valid color is generated
|
||||||
|
# a valid color has the # symbol, another 6 chars for the color and the last 2 chars for alpha
|
||||||
|
# for a total of 9 chars
|
||||||
|
if len(new_color) == 9:
|
||||||
|
break
|
||||||
|
return new_color
|
||||||
|
|
||||||
# try:
|
# try:
|
||||||
# # Plot Excellon (All polygons?)
|
# # Plot Excellon (All polygons?)
|
||||||
# if self.options["solid"]:
|
# if self.options["solid"]:
|
||||||
@@ -1576,12 +1887,26 @@ class ExcellonObject(FlatCAMObj, Excellon):
|
|||||||
try:
|
try:
|
||||||
# Plot Excellon (All polygons?)
|
# Plot Excellon (All polygons?)
|
||||||
if self.options["solid"]:
|
if self.options["solid"]:
|
||||||
for geo in self.solid_geometry:
|
# for geo in self.solid_geometry:
|
||||||
|
# self.add_shape(shape=geo,
|
||||||
|
# color=self.outline_color,
|
||||||
|
# face_color=random_color() if self.options['multicolored'] else self.fill_color,
|
||||||
|
# visible=visible,
|
||||||
|
# layer=2)
|
||||||
|
|
||||||
|
# plot polygons for each tool separately
|
||||||
|
for tool in self.tools:
|
||||||
|
# set the color here so we have one color for each tool
|
||||||
|
geo_color = random_color()
|
||||||
|
|
||||||
|
# tool is a dict also
|
||||||
|
for geo in self.tools[tool]["solid_geometry"]:
|
||||||
self.add_shape(shape=geo,
|
self.add_shape(shape=geo,
|
||||||
color=self.outline_color,
|
color=geo_color if self.options['multicolored'] else self.outline_color,
|
||||||
face_color=self.fill_color,
|
face_color=geo_color if self.options['multicolored'] else self.fill_color,
|
||||||
visible=visible,
|
visible=visible,
|
||||||
layer=2)
|
layer=2)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
for geo in self.solid_geometry:
|
for geo in self.solid_geometry:
|
||||||
self.add_shape(shape=geo.exterior, color='red', visible=visible)
|
self.add_shape(shape=geo.exterior, color='red', visible=visible)
|
||||||
@@ -15,8 +15,7 @@ import shapely.affinity as affinity
|
|||||||
|
|
||||||
from camlib import Geometry
|
from camlib import Geometry
|
||||||
|
|
||||||
from flatcamObjects.FlatCAMObj import *
|
from AppObjects.FlatCAMObj import *
|
||||||
import FlatCAMTool
|
|
||||||
|
|
||||||
import ezdxf
|
import ezdxf
|
||||||
import math
|
import math
|
||||||
@@ -25,7 +24,7 @@ from copy import deepcopy
|
|||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -53,6 +52,7 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
|
|
||||||
self.options.update({
|
self.options.update({
|
||||||
"plot": True,
|
"plot": True,
|
||||||
|
"multicolored": False,
|
||||||
"cutz": -0.002,
|
"cutz": -0.002,
|
||||||
"vtipdia": 0.1,
|
"vtipdia": 0.1,
|
||||||
"vtipangle": 30,
|
"vtipangle": 30,
|
||||||
@@ -151,17 +151,9 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
|
|
||||||
self.param_fields = {}
|
self.param_fields = {}
|
||||||
|
|
||||||
# Event signals disconnect id holders
|
# store here the state of the exclusion checkbox state to be restored after building the UI
|
||||||
self.mr = None
|
# TODO add this in the sel.app.defaults dict and in Preferences
|
||||||
self.mm = None
|
self.exclusion_area_cb_is_checked = False
|
||||||
self.kp = None
|
|
||||||
|
|
||||||
# variables to be used in area exclusion
|
|
||||||
self.cursor_pos = (0, 0)
|
|
||||||
self.exclusion_areas_list = []
|
|
||||||
self.first_click = False
|
|
||||||
self.points = []
|
|
||||||
self.poly_drawn = False
|
|
||||||
|
|
||||||
# Attributes to be included in serialization
|
# Attributes to be included in serialization
|
||||||
# Always append to it because it carries contents
|
# Always append to it because it carries contents
|
||||||
@@ -172,6 +164,15 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
self.ui_disconnect()
|
self.ui_disconnect()
|
||||||
FlatCAMObj.build_ui(self)
|
FlatCAMObj.build_ui(self)
|
||||||
|
|
||||||
|
# Area Exception - exclusion shape added signal
|
||||||
|
# first disconnect it from any other object
|
||||||
|
try:
|
||||||
|
self.app.exc_areas.e_shape_modified.disconnect()
|
||||||
|
except (TypeError, AttributeError):
|
||||||
|
pass
|
||||||
|
# then connect it to the current build_ui() method
|
||||||
|
self.app.exc_areas.e_shape_modified.connect(self.update_exclusion_table)
|
||||||
|
|
||||||
self.units = self.app.defaults['units']
|
self.units = self.app.defaults['units']
|
||||||
|
|
||||||
tool_idx = 0
|
tool_idx = 0
|
||||||
@@ -192,7 +193,6 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
# For INCH the decimals should be no more than 3. There are no tools under 10mils.
|
# For INCH the decimals should be no more than 3. There are no tools under 10mils.
|
||||||
|
|
||||||
dia_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, float(tooluid_value['tooldia'])))
|
dia_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, float(tooluid_value['tooldia'])))
|
||||||
|
|
||||||
dia_item.setFlags(QtCore.Qt.ItemIsEnabled)
|
dia_item.setFlags(QtCore.Qt.ItemIsEnabled)
|
||||||
|
|
||||||
offset_item = FCComboBox()
|
offset_item = FCComboBox()
|
||||||
@@ -305,6 +305,63 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
|
|
||||||
self.set_tool_offset_visibility(selected_row)
|
self.set_tool_offset_visibility(selected_row)
|
||||||
|
|
||||||
|
# -----------------------------
|
||||||
|
# Build Exclusion Areas section
|
||||||
|
# -----------------------------
|
||||||
|
e_len = len(self.app.exc_areas.exclusion_areas_storage)
|
||||||
|
self.ui.exclusion_table.setRowCount(e_len)
|
||||||
|
|
||||||
|
area_id = 0
|
||||||
|
|
||||||
|
for area in range(e_len):
|
||||||
|
area_id += 1
|
||||||
|
|
||||||
|
area_dict = self.app.exc_areas.exclusion_areas_storage[area]
|
||||||
|
|
||||||
|
area_id_item = QtWidgets.QTableWidgetItem('%d' % int(area_id))
|
||||||
|
area_id_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||||
|
self.ui.exclusion_table.setItem(area, 0, area_id_item) # Area id
|
||||||
|
|
||||||
|
object_item = QtWidgets.QTableWidgetItem('%s' % area_dict["obj_type"])
|
||||||
|
object_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||||
|
self.ui.exclusion_table.setItem(area, 1, object_item) # Origin Object
|
||||||
|
|
||||||
|
strategy_item = QtWidgets.QTableWidgetItem('%s' % area_dict["strategy"])
|
||||||
|
strategy_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||||
|
self.ui.exclusion_table.setItem(area, 2, strategy_item) # Strategy
|
||||||
|
|
||||||
|
overz_item = QtWidgets.QTableWidgetItem('%s' % area_dict["overz"])
|
||||||
|
overz_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||||
|
self.ui.exclusion_table.setItem(area, 3, overz_item) # Over Z
|
||||||
|
|
||||||
|
self.ui.exclusion_table.resizeColumnsToContents()
|
||||||
|
self.ui.exclusion_table.resizeRowsToContents()
|
||||||
|
|
||||||
|
area_vheader = self.ui.exclusion_table.verticalHeader()
|
||||||
|
area_vheader.hide()
|
||||||
|
self.ui.exclusion_table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||||
|
|
||||||
|
area_hheader = self.ui.exclusion_table.horizontalHeader()
|
||||||
|
area_hheader.setMinimumSectionSize(10)
|
||||||
|
area_hheader.setDefaultSectionSize(70)
|
||||||
|
|
||||||
|
area_hheader.setSectionResizeMode(0, QtWidgets.QHeaderView.Fixed)
|
||||||
|
area_hheader.resizeSection(0, 20)
|
||||||
|
area_hheader.setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch)
|
||||||
|
area_hheader.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents)
|
||||||
|
area_hheader.setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeToContents)
|
||||||
|
|
||||||
|
# area_hheader.setStretchLastSection(True)
|
||||||
|
self.ui.exclusion_table.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||||
|
|
||||||
|
self.ui.exclusion_table.setColumnWidth(0, 20)
|
||||||
|
|
||||||
|
self.ui.exclusion_table.setMinimumHeight(self.ui.exclusion_table.getHeight())
|
||||||
|
self.ui.exclusion_table.setMaximumHeight(self.ui.exclusion_table.getHeight())
|
||||||
|
|
||||||
|
# End Build Exclusion Areas
|
||||||
|
# -----------------------------
|
||||||
|
|
||||||
# HACK: for whatever reasons the name in Selected tab is reverted to the original one after a successful rename
|
# HACK: for whatever reasons the name in Selected tab is reverted to the original one after a successful rename
|
||||||
# done in the collection view but only for Geometry objects. Perhaps some references remains. Should be fixed.
|
# done in the collection view but only for Geometry objects. Perhaps some references remains. Should be fixed.
|
||||||
self.ui.name_entry.set_value(self.options['name'])
|
self.ui.name_entry.set_value(self.options['name'])
|
||||||
@@ -340,6 +397,7 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
|
|
||||||
self.form_fields.update({
|
self.form_fields.update({
|
||||||
"plot": self.ui.plot_cb,
|
"plot": self.ui.plot_cb,
|
||||||
|
"multicolored": self.ui.multicolored_cb,
|
||||||
"cutz": self.ui.cutz_entry,
|
"cutz": self.ui.cutz_entry,
|
||||||
"vtipdia": self.ui.tipdia_entry,
|
"vtipdia": self.ui.tipdia_entry,
|
||||||
"vtipangle": self.ui.tipangle_entry,
|
"vtipangle": self.ui.tipangle_entry,
|
||||||
@@ -363,7 +421,7 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
"endxy": self.ui.endxy_entry,
|
"endxy": self.ui.endxy_entry,
|
||||||
"cnctooldia": self.ui.addtool_entry,
|
"cnctooldia": self.ui.addtool_entry,
|
||||||
"area_exclusion": self.ui.exclusion_cb,
|
"area_exclusion": self.ui.exclusion_cb,
|
||||||
"area_shape":self.ui.area_shape_radio,
|
"area_shape": self.ui.area_shape_radio,
|
||||||
"area_strategy": self.ui.strategy_radio,
|
"area_strategy": self.ui.strategy_radio,
|
||||||
"area_overz": self.ui.over_z_entry,
|
"area_overz": self.ui.over_z_entry,
|
||||||
})
|
})
|
||||||
@@ -535,6 +593,8 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
self.ui.extracut_cb.toggled.connect(lambda state: self.ui.e_cut_entry.setDisabled(not state))
|
self.ui.extracut_cb.toggled.connect(lambda state: self.ui.e_cut_entry.setDisabled(not state))
|
||||||
|
|
||||||
self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click)
|
self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click)
|
||||||
|
self.ui.multicolored_cb.stateChanged.connect(self.on_multicolored_cb_click)
|
||||||
|
|
||||||
self.ui.generate_cnc_button.clicked.connect(self.on_generatecnc_button_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.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.generate_ncc_button.clicked.connect(lambda: self.app.ncclear_tool.run(toggle=False))
|
||||||
@@ -547,8 +607,14 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
self.ui.apply_param_to_all.clicked.connect(self.on_apply_param_to_all_clicked)
|
self.ui.apply_param_to_all.clicked.connect(self.on_apply_param_to_all_clicked)
|
||||||
self.ui.cutz_entry.returnPressed.connect(self.on_cut_z_changed)
|
self.ui.cutz_entry.returnPressed.connect(self.on_cut_z_changed)
|
||||||
|
|
||||||
|
# Exclusion areas signals
|
||||||
|
self.ui.exclusion_table.horizontalHeader().sectionClicked.connect(self.exclusion_table_toggle_all)
|
||||||
|
self.ui.exclusion_table.lost_focus.connect(self.clear_selection)
|
||||||
|
self.ui.exclusion_table.itemClicked.connect(self.draw_sel_shape)
|
||||||
self.ui.add_area_button.clicked.connect(self.on_add_area_click)
|
self.ui.add_area_button.clicked.connect(self.on_add_area_click)
|
||||||
self.ui.delete_area_button.clicked.connect(self.on_clear_area_click)
|
self.ui.delete_area_button.clicked.connect(self.on_clear_area_click)
|
||||||
|
self.ui.delete_sel_area_button.clicked.connect(self.on_delete_sel_areas)
|
||||||
|
self.ui.strategy_radio.activated_custom.connect(self.on_strategy)
|
||||||
|
|
||||||
def on_cut_z_changed(self):
|
def on_cut_z_changed(self):
|
||||||
self.old_cutz = self.ui.cutz_entry.get_value()
|
self.old_cutz = self.ui.cutz_entry.get_value()
|
||||||
@@ -595,8 +661,7 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
self.ui.tool_offset_entry.get_value().replace(',', '.')
|
self.ui.tool_offset_entry.get_value().replace(',', '.')
|
||||||
)
|
)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
self.app.inform.emit('[ERROR_NOTCL] %s' % _("Wrong value format entered, use a number."))
|
||||||
_("Wrong value format entered, use a number."))
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def ui_connect(self):
|
def ui_connect(self):
|
||||||
@@ -604,6 +669,7 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
# changes in geometry UI
|
# changes in geometry UI
|
||||||
for i in self.param_fields:
|
for i in self.param_fields:
|
||||||
current_widget = self.param_fields[i]
|
current_widget = self.param_fields[i]
|
||||||
|
|
||||||
if isinstance(current_widget, FCCheckBox):
|
if isinstance(current_widget, FCCheckBox):
|
||||||
current_widget.stateChanged.connect(self.gui_form_to_storage)
|
current_widget.stateChanged.connect(self.gui_form_to_storage)
|
||||||
elif isinstance(current_widget, FCComboBox):
|
elif isinstance(current_widget, FCComboBox):
|
||||||
@@ -874,7 +940,7 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
self.ui_connect()
|
self.ui_connect()
|
||||||
self.build_ui()
|
self.build_ui()
|
||||||
|
|
||||||
# if there is no tool left in the Tools Table, enable the parameters GUI
|
# if there is no tool left in the Tools Table, enable the parameters AppGUI
|
||||||
if self.ui.geo_tools_table.rowCount() != 0:
|
if self.ui.geo_tools_table.rowCount() != 0:
|
||||||
self.ui.geo_param_frame.setDisabled(False)
|
self.ui.geo_param_frame.setDisabled(False)
|
||||||
|
|
||||||
@@ -950,7 +1016,7 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
self.ui_connect()
|
self.ui_connect()
|
||||||
self.build_ui()
|
self.build_ui()
|
||||||
|
|
||||||
# if there is no tool left in the Tools Table, enable the parameters GUI
|
# if there is no tool left in the Tools Table, enable the parameters AppGUI
|
||||||
if self.ui.geo_tools_table.rowCount() != 0:
|
if self.ui.geo_tools_table.rowCount() != 0:
|
||||||
self.ui.geo_param_frame.setDisabled(False)
|
self.ui.geo_param_frame.setDisabled(False)
|
||||||
|
|
||||||
@@ -1131,7 +1197,7 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
obj_active.options['xmax'] = 0
|
obj_active.options['xmax'] = 0
|
||||||
obj_active.options['ymax'] = 0
|
obj_active.options['ymax'] = 0
|
||||||
|
|
||||||
# if there is no tool left in the Tools Table, disable the parameters GUI
|
# if there is no tool left in the Tools Table, disable the parameters AppGUI
|
||||||
if self.ui.geo_tools_table.rowCount() == 0:
|
if self.ui.geo_tools_table.rowCount() == 0:
|
||||||
self.ui.geo_param_frame.setDisabled(True)
|
self.ui.geo_param_frame.setDisabled(True)
|
||||||
|
|
||||||
@@ -1149,8 +1215,7 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
"- 'V-tip Angle' -> angle at the tip of the tool\n"
|
"- 'V-tip Angle' -> angle at the tip of the tool\n"
|
||||||
"- 'V-tip Dia' -> diameter at the tip of the tool \n"
|
"- 'V-tip Dia' -> diameter at the tip of the tool \n"
|
||||||
"- Tool Dia -> 'Dia' column found in the Tool Table\n"
|
"- Tool Dia -> 'Dia' column found in the Tool Table\n"
|
||||||
"NB: a value of zero means that Tool Dia = 'V-tip Dia'"
|
"NB: a value of zero means that Tool Dia = 'V-tip Dia'")
|
||||||
)
|
|
||||||
)
|
)
|
||||||
self.ui.cutz_entry.setToolTip(
|
self.ui.cutz_entry.setToolTip(
|
||||||
_("Disabled because the tool is V-shape.\n"
|
_("Disabled because the tool is V-shape.\n"
|
||||||
@@ -1159,8 +1224,7 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
"- 'V-tip Angle' -> angle at the tip of the tool\n"
|
"- 'V-tip Angle' -> angle at the tip of the tool\n"
|
||||||
"- 'V-tip Dia' -> diameter at the tip of the tool \n"
|
"- 'V-tip Dia' -> diameter at the tip of the tool \n"
|
||||||
"- Tool Dia -> 'Dia' column found in the Tool Table\n"
|
"- Tool Dia -> 'Dia' column found in the Tool Table\n"
|
||||||
"NB: a value of zero means that Tool Dia = 'V-tip Dia'"
|
"NB: a value of zero means that Tool Dia = 'V-tip Dia'")
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.update_cutz()
|
self.update_cutz()
|
||||||
@@ -1172,8 +1236,7 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
self.ui.cutz_entry.setDisabled(False)
|
self.ui.cutz_entry.setDisabled(False)
|
||||||
self.ui.cutzlabel.setToolTip(
|
self.ui.cutzlabel.setToolTip(
|
||||||
_("Cutting depth (negative)\n"
|
_("Cutting depth (negative)\n"
|
||||||
"below the copper surface."
|
"below the copper surface.")
|
||||||
)
|
|
||||||
)
|
)
|
||||||
self.ui.cutz_entry.setToolTip('')
|
self.ui.cutz_entry.setToolTip('')
|
||||||
|
|
||||||
@@ -1328,16 +1391,18 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
self.ui_connect()
|
self.ui_connect()
|
||||||
|
|
||||||
def gui_form_to_storage(self):
|
def gui_form_to_storage(self):
|
||||||
|
self.ui_disconnect()
|
||||||
|
|
||||||
if self.ui.geo_tools_table.rowCount() == 0:
|
if self.ui.geo_tools_table.rowCount() == 0:
|
||||||
# there is no tool in tool table so we can't save the GUI elements values to storage
|
# there is no tool in tool table so we can't save the GUI elements values to storage
|
||||||
log.debug("GeometryObject.gui_form_to_storage() --> no tool in Tools Table, aborting.")
|
log.debug("GeometryObject.gui_form_to_storage() --> no tool in Tools Table, aborting.")
|
||||||
return
|
return
|
||||||
|
|
||||||
self.ui_disconnect()
|
|
||||||
widget_changed = self.sender()
|
widget_changed = self.sender()
|
||||||
try:
|
try:
|
||||||
widget_idx = self.ui.grid3.indexOf(widget_changed)
|
widget_idx = self.ui.grid3.indexOf(widget_changed)
|
||||||
except Exception:
|
except Exception as e:
|
||||||
|
log.debug("GeometryObject.gui_form_to_storage() -- wdg index -> %s" % str(e))
|
||||||
return
|
return
|
||||||
|
|
||||||
# those are the indexes for the V-Tip Dia and V-Tip Angle, if edited calculate the new Cut Z
|
# those are the indexes for the V-Tip Dia and V-Tip Angle, if edited calculate the new Cut Z
|
||||||
@@ -1690,15 +1755,11 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
The actual work is done by the target CNCJobObject object's
|
The actual work is done by the target CNCJobObject object's
|
||||||
`generate_from_geometry_2()` method.
|
`generate_from_geometry_2()` method.
|
||||||
|
|
||||||
|
:param outname:
|
||||||
:param tools_dict: a dictionary that holds the whole data needed to create the Gcode
|
:param tools_dict: a dictionary that holds the whole data needed to create the Gcode
|
||||||
(including the solid_geometry)
|
(including the solid_geometry)
|
||||||
|
|
||||||
:param tools_in_use: the tools that are used, needed by some preprocessors
|
:param tools_in_use: the tools that are used, needed by some preprocessors
|
||||||
:type list of lists, each list in the list is made out of row elements of tools table from GUI
|
:type tools_in_use list of lists, each list in the list is made out of row elements of tools table from AppGUI
|
||||||
|
|
||||||
:param outname:
|
|
||||||
:param tools_dict:
|
|
||||||
:param tools_in_use:
|
|
||||||
:param segx: number of segments on the X axis, for auto-levelling
|
:param segx: number of segments on the X axis, for auto-levelling
|
||||||
:param segy: number of segments on the Y axis, for auto-levelling
|
:param segy: number of segments on the Y axis, for auto-levelling
|
||||||
:param plot: if True the generated object will be plotted; if False will not be plotted
|
:param plot: if True the generated object will be plotted; if False will not be plotted
|
||||||
@@ -1728,7 +1789,7 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
self.app.inform.emit(msg)
|
self.app.inform.emit(msg)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Object initialization function for app.new_object()
|
# Object initialization function for app.app_obj.new_object()
|
||||||
# RUNNING ON SEPARATE THREAD!
|
# RUNNING ON SEPARATE THREAD!
|
||||||
def job_init_single_geometry(job_obj, app_obj):
|
def job_init_single_geometry(job_obj, app_obj):
|
||||||
log.debug("Creating a CNCJob out of a single-geometry")
|
log.debug("Creating a CNCJob out of a single-geometry")
|
||||||
@@ -1742,7 +1803,7 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
# count the tools
|
# count the tools
|
||||||
tool_cnt = 0
|
tool_cnt = 0
|
||||||
|
|
||||||
dia_cnc_dict = {}
|
# dia_cnc_dict = {}
|
||||||
|
|
||||||
# this turn on the FlatCAMCNCJob plot for multiple tools
|
# this turn on the FlatCAMCNCJob plot for multiple tools
|
||||||
job_obj.multitool = True
|
job_obj.multitool = True
|
||||||
@@ -1868,7 +1929,7 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
})
|
})
|
||||||
dia_cnc_dict.clear()
|
dia_cnc_dict.clear()
|
||||||
|
|
||||||
# Object initialization function for app.new_object()
|
# Object initialization function for app.app_obj.new_object()
|
||||||
# RUNNING ON SEPARATE THREAD!
|
# RUNNING ON SEPARATE THREAD!
|
||||||
def job_init_multi_geometry(job_obj, app_obj):
|
def job_init_multi_geometry(job_obj, app_obj):
|
||||||
log.debug("Creating a CNCJob out of a multi-geometry")
|
log.debug("Creating a CNCJob out of a multi-geometry")
|
||||||
@@ -1882,7 +1943,7 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
# count the tools
|
# count the tools
|
||||||
tool_cnt = 0
|
tool_cnt = 0
|
||||||
|
|
||||||
dia_cnc_dict = {}
|
# dia_cnc_dict = {}
|
||||||
|
|
||||||
# this turn on the FlatCAMCNCJob plot for multiple tools
|
# this turn on the FlatCAMCNCJob plot for multiple tools
|
||||||
job_obj.multitool = True
|
job_obj.multitool = True
|
||||||
@@ -2022,15 +2083,15 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
|
|
||||||
if use_thread:
|
if use_thread:
|
||||||
# To be run in separate thread
|
# To be run in separate thread
|
||||||
def job_thread(app_obj):
|
def job_thread(a_obj):
|
||||||
if self.multigeo is False:
|
if self.multigeo is False:
|
||||||
with self.app.proc_container.new(_("Generating CNC Code")):
|
with self.app.proc_container.new(_("Generating CNC Code")):
|
||||||
if app_obj.new_object("cncjob", outname, job_init_single_geometry, plot=plot) != 'fail':
|
if a_obj.app_obj.new_object("cncjob", outname, job_init_single_geometry, plot=plot) != 'fail':
|
||||||
app_obj.inform.emit('[success] %s: %s' % (_("CNCjob created"), outname))
|
a_obj.inform.emit('[success] %s: %s' % (_("CNCjob created"), outname))
|
||||||
else:
|
else:
|
||||||
with self.app.proc_container.new(_("Generating CNC Code")):
|
with self.app.proc_container.new(_("Generating CNC Code")):
|
||||||
if app_obj.new_object("cncjob", outname, job_init_multi_geometry) != 'fail':
|
if a_obj.app_obj.new_object("cncjob", outname, job_init_multi_geometry) != 'fail':
|
||||||
app_obj.inform.emit('[success] %s: %s' % (_("CNCjob created"), outname))
|
a_obj.inform.emit('[success] %s: %s' % (_("CNCjob created"), outname))
|
||||||
|
|
||||||
# Create a promise with the name
|
# Create a promise with the name
|
||||||
self.app.collection.promise(outname)
|
self.app.collection.promise(outname)
|
||||||
@@ -2038,25 +2099,17 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
|
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
|
||||||
else:
|
else:
|
||||||
if self.solid_geometry:
|
if self.solid_geometry:
|
||||||
self.app.new_object("cncjob", outname, job_init_single_geometry, plot=plot)
|
self.app.app_obj.new_object("cncjob", outname, job_init_single_geometry, plot=plot)
|
||||||
else:
|
else:
|
||||||
self.app.new_object("cncjob", outname, job_init_multi_geometry, plot=plot)
|
self.app.app_obj.new_object("cncjob", outname, job_init_multi_geometry, plot=plot)
|
||||||
|
|
||||||
def generatecncjob(
|
def generatecncjob(self, outname=None, dia=None, offset=None, z_cut=None, z_move=None,
|
||||||
self, outname=None,
|
feedrate=None, feedrate_z=None, feedrate_rapid=None, spindlespeed=None, dwell=None, dwelltime=None,
|
||||||
dia=None, offset=None,
|
multidepth=None, dpp=None, toolchange=None, toolchangez=None, toolchangexy=None,
|
||||||
z_cut=None, z_move=None,
|
extracut=None, extracut_length=None, startz=None, endz=None, endxy=None, pp=None, segx=None, segy=None,
|
||||||
feedrate=None, feedrate_z=None, feedrate_rapid=None,
|
use_thread=True, plot=True):
|
||||||
spindlespeed=None, dwell=None, dwelltime=None,
|
|
||||||
multidepth=None, depthperpass=None,
|
|
||||||
toolchange=None, toolchangez=None, toolchangexy=None,
|
|
||||||
extracut=None, extracut_length=None, startz=None, endz=None,
|
|
||||||
pp=None,
|
|
||||||
segx=None, segy=None,
|
|
||||||
use_thread=True,
|
|
||||||
plot=True):
|
|
||||||
"""
|
"""
|
||||||
Only used for TCL Command.
|
Only used by the TCL Command Cncjob.
|
||||||
Creates a CNCJob out of this Geometry object. The actual
|
Creates a CNCJob out of this Geometry object. The actual
|
||||||
work is done by the target camlib.CNCjob
|
work is done by the target camlib.CNCjob
|
||||||
`generate_from_geometry_2()` method.
|
`generate_from_geometry_2()` method.
|
||||||
@@ -2073,14 +2126,17 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
:param dwell:
|
:param dwell:
|
||||||
:param dwelltime:
|
:param dwelltime:
|
||||||
:param multidepth:
|
:param multidepth:
|
||||||
:param depthperpass:
|
:param dpp: Depth for each pass when multidepth parameter is True
|
||||||
:param toolchange:
|
:param toolchange:
|
||||||
:param toolchangez:
|
:param toolchangez:
|
||||||
:param toolchangexy:
|
:param toolchangexy: A sequence ox X,Y coordinates: a 2-length tuple or a string.
|
||||||
|
Coordinates in X,Y plane for the Toolchange event
|
||||||
:param extracut:
|
:param extracut:
|
||||||
:param extracut_length:
|
:param extracut_length:
|
||||||
:param startz:
|
:param startz:
|
||||||
:param endz:
|
:param endz:
|
||||||
|
:param endxy: A sequence ox X,Y coordinates: a 2-length tuple or a string.
|
||||||
|
Coordinates in X, Y plane for the last move after ending the job.
|
||||||
:param pp: Name of the preprocessor
|
:param pp: Name of the preprocessor
|
||||||
:param segx:
|
:param segx:
|
||||||
:param segy:
|
:param segy:
|
||||||
@@ -2100,7 +2156,7 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
feedrate_rapid = feedrate_rapid if feedrate_rapid is not None else float(self.options["feedrate_rapid"])
|
feedrate_rapid = feedrate_rapid if feedrate_rapid is not None else float(self.options["feedrate_rapid"])
|
||||||
|
|
||||||
multidepth = multidepth if multidepth is not None else self.options["multidepth"]
|
multidepth = multidepth if multidepth is not None else self.options["multidepth"]
|
||||||
depthperpass = depthperpass if depthperpass is not None else float(self.options["depthperpass"])
|
depthperpass = dpp if dpp is not None else float(self.options["depthperpass"])
|
||||||
|
|
||||||
segx = segx if segx is not None else float(self.app.defaults['geometry_segx'])
|
segx = segx if segx is not None else float(self.app.defaults['geometry_segx'])
|
||||||
segy = segy if segy is not None else float(self.app.defaults['geometry_segy'])
|
segy = segy if segy is not None else float(self.app.defaults['geometry_segy'])
|
||||||
@@ -2110,10 +2166,21 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
|
|
||||||
startz = startz if startz is not None else self.options["startz"]
|
startz = startz if startz is not None else self.options["startz"]
|
||||||
endz = endz if endz is not None else float(self.options["endz"])
|
endz = endz if endz is not None else float(self.options["endz"])
|
||||||
endxy = self.options["endxy"]
|
|
||||||
|
endxy = endxy if endxy else self.options["endxy"]
|
||||||
|
if isinstance(endxy, str):
|
||||||
|
endxy = re.sub('[()\[\]]', '', endxy)
|
||||||
|
if endxy and endxy != '':
|
||||||
|
endxy = [float(eval(a)) for a in endxy.split(",")]
|
||||||
|
|
||||||
toolchangez = toolchangez if toolchangez else float(self.options["toolchangez"])
|
toolchangez = toolchangez if toolchangez else float(self.options["toolchangez"])
|
||||||
|
|
||||||
toolchangexy = toolchangexy if toolchangexy else self.options["toolchangexy"]
|
toolchangexy = toolchangexy if toolchangexy else self.options["toolchangexy"]
|
||||||
|
if isinstance(toolchangexy, str):
|
||||||
|
toolchangexy = re.sub('[()\[\]]', '', toolchangexy)
|
||||||
|
if toolchangexy and toolchangexy != '':
|
||||||
|
toolchangexy = [float(eval(a)) for a in toolchangexy.split(",")]
|
||||||
|
|
||||||
toolchange = toolchange if toolchange else self.options["toolchange"]
|
toolchange = toolchange if toolchange else self.options["toolchange"]
|
||||||
|
|
||||||
offset = offset if offset else 0.0
|
offset = offset if offset else 0.0
|
||||||
@@ -2125,7 +2192,7 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
|
|
||||||
ppname_g = pp if pp else self.options["ppname_g"]
|
ppname_g = pp if pp else self.options["ppname_g"]
|
||||||
|
|
||||||
# Object initialization function for app.new_object()
|
# Object initialization function for app.app_obj.new_object()
|
||||||
# RUNNING ON SEPARATE THREAD!
|
# RUNNING ON SEPARATE THREAD!
|
||||||
def job_init(job_obj, app_obj):
|
def job_init(job_obj, app_obj):
|
||||||
assert job_obj.kind == 'cncjob', "Initializer expected a CNCJobObject, got %s" % type(job_obj)
|
assert job_obj.kind == 'cncjob', "Initializer expected a CNCJobObject, got %s" % type(job_obj)
|
||||||
@@ -2174,7 +2241,7 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
# To be run in separate thread
|
# To be run in separate thread
|
||||||
def job_thread(app_obj):
|
def job_thread(app_obj):
|
||||||
with self.app.proc_container.new(_("Generating CNC Code")):
|
with self.app.proc_container.new(_("Generating CNC Code")):
|
||||||
app_obj.new_object("cncjob", outname, job_init, plot=plot)
|
app_obj.app_obj.new_object("cncjob", outname, job_init, plot=plot)
|
||||||
app_obj.inform.emit('[success] %s: %s' % (_("CNCjob created")), outname)
|
app_obj.inform.emit('[success] %s: %s' % (_("CNCjob created")), outname)
|
||||||
|
|
||||||
# Create a promise with the name
|
# Create a promise with the name
|
||||||
@@ -2182,7 +2249,7 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
# Send to worker
|
# Send to worker
|
||||||
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
|
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
|
||||||
else:
|
else:
|
||||||
self.app.new_object("cncjob", outname, job_init, plot=plot)
|
self.app.app_obj.new_object("cncjob", outname, job_init, plot=plot)
|
||||||
|
|
||||||
# def on_plot_cb_click(self, *args):
|
# def on_plot_cb_click(self, *args):
|
||||||
# if self.muted_ui:
|
# if self.muted_ui:
|
||||||
@@ -2469,6 +2536,110 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
|
|
||||||
return factor
|
return factor
|
||||||
|
|
||||||
|
def on_add_area_click(self):
|
||||||
|
shape_button = self.ui.area_shape_radio
|
||||||
|
overz_button = self.ui.over_z_entry
|
||||||
|
strategy_radio = self.ui.strategy_radio
|
||||||
|
cnc_button = self.ui.generate_cnc_button
|
||||||
|
solid_geo = self.solid_geometry
|
||||||
|
obj_type = self.kind
|
||||||
|
|
||||||
|
self.app.exc_areas.on_add_area_click(
|
||||||
|
shape_button=shape_button, overz_button=overz_button, cnc_button=cnc_button, strategy_radio=strategy_radio,
|
||||||
|
solid_geo=solid_geo, obj_type=obj_type)
|
||||||
|
|
||||||
|
def on_clear_area_click(self):
|
||||||
|
if not self.app.exc_areas.exclusion_areas_storage:
|
||||||
|
self.app.inform.emit("[WARNING_NOTCL] %s" % _("Delete failed. There are no exclusion areas to delete."))
|
||||||
|
return
|
||||||
|
|
||||||
|
self.app.exc_areas.on_clear_area_click()
|
||||||
|
self.app.exc_areas.e_shape_modified.emit()
|
||||||
|
|
||||||
|
def on_delete_sel_areas(self):
|
||||||
|
sel_model = self.ui.exclusion_table.selectionModel()
|
||||||
|
sel_indexes = sel_model.selectedIndexes()
|
||||||
|
|
||||||
|
# it will iterate over all indexes which means all items in all columns too but I'm interested only on rows
|
||||||
|
# so the duplicate rows will not be added
|
||||||
|
sel_rows = set()
|
||||||
|
for idx in sel_indexes:
|
||||||
|
sel_rows.add(idx.row())
|
||||||
|
|
||||||
|
if not sel_rows:
|
||||||
|
self.app.inform.emit("[WARNING_NOTCL] %s" % _("Delete failed. Nothing is selected."))
|
||||||
|
return
|
||||||
|
|
||||||
|
self.app.exc_areas.delete_sel_shapes(idxs=list(sel_rows))
|
||||||
|
self.app.exc_areas.e_shape_modified.emit()
|
||||||
|
|
||||||
|
def draw_sel_shape(self):
|
||||||
|
sel_model = self.ui.exclusion_table.selectionModel()
|
||||||
|
sel_indexes = sel_model.selectedIndexes()
|
||||||
|
|
||||||
|
# it will iterate over all indexes which means all items in all columns too but I'm interested only on rows
|
||||||
|
sel_rows = set()
|
||||||
|
for idx in sel_indexes:
|
||||||
|
sel_rows.add(idx.row())
|
||||||
|
|
||||||
|
self.delete_sel_shape()
|
||||||
|
|
||||||
|
if self.app.is_legacy is False:
|
||||||
|
face = self.app.defaults['global_sel_fill'][:-2] + str(hex(int(0.2 * 255)))[2:]
|
||||||
|
outline = self.app.defaults['global_sel_line'][:-2] + str(hex(int(0.8 * 255)))[2:]
|
||||||
|
else:
|
||||||
|
face = self.app.defaults['global_sel_fill'][:-2] + str(hex(int(0.4 * 255)))[2:]
|
||||||
|
outline = self.app.defaults['global_sel_line'][:-2] + str(hex(int(1.0 * 255)))[2:]
|
||||||
|
|
||||||
|
for row in sel_rows:
|
||||||
|
sel_rect = self.app.exc_areas.exclusion_areas_storage[row]['shape']
|
||||||
|
self.app.move_tool.sel_shapes.add(sel_rect, color=outline, face_color=face, update=True, layer=0,
|
||||||
|
tolerance=None)
|
||||||
|
if self.app.is_legacy is True:
|
||||||
|
self.app.move_tool.sel_shapes.redraw()
|
||||||
|
|
||||||
|
def clear_selection(self):
|
||||||
|
self.app.delete_selection_shape()
|
||||||
|
# self.ui.exclusion_table.clearSelection()
|
||||||
|
|
||||||
|
def delete_sel_shape(self):
|
||||||
|
self.app.delete_selection_shape()
|
||||||
|
|
||||||
|
def update_exclusion_table(self):
|
||||||
|
self.exclusion_area_cb_is_checked = True if self.ui.exclusion_cb.isChecked() else False
|
||||||
|
|
||||||
|
self.build_ui()
|
||||||
|
self.ui.exclusion_cb.set_value(self.exclusion_area_cb_is_checked)
|
||||||
|
|
||||||
|
def on_strategy(self, val):
|
||||||
|
if val == 'around':
|
||||||
|
self.ui.over_z_label.setDisabled(True)
|
||||||
|
self.ui.over_z_entry.setDisabled(True)
|
||||||
|
else:
|
||||||
|
self.ui.over_z_label.setDisabled(False)
|
||||||
|
self.ui.over_z_entry.setDisabled(False)
|
||||||
|
|
||||||
|
def exclusion_table_toggle_all(self):
|
||||||
|
"""
|
||||||
|
will toggle the selection of all rows in Exclusion Areas table
|
||||||
|
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
sel_model = self.ui.exclusion_table.selectionModel()
|
||||||
|
sel_indexes = sel_model.selectedIndexes()
|
||||||
|
|
||||||
|
# it will iterate over all indexes which means all items in all columns too but I'm interested only on rows
|
||||||
|
sel_rows = set()
|
||||||
|
for idx in sel_indexes:
|
||||||
|
sel_rows.add(idx.row())
|
||||||
|
|
||||||
|
if sel_rows:
|
||||||
|
self.ui.exclusion_table.clearSelection()
|
||||||
|
self.delete_sel_shape()
|
||||||
|
else:
|
||||||
|
self.ui.exclusion_table.selectAll()
|
||||||
|
self.draw_sel_shape()
|
||||||
|
|
||||||
def plot_element(self, element, color=None, visible=None):
|
def plot_element(self, element, color=None, visible=None):
|
||||||
|
|
||||||
if color is None:
|
if color is None:
|
||||||
@@ -2498,6 +2669,27 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
if not FlatCAMObj.plot(self):
|
if not FlatCAMObj.plot(self):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if self.app.is_legacy is False:
|
||||||
|
def random_color():
|
||||||
|
r_color = np.random.rand(4)
|
||||||
|
r_color[3] = 1
|
||||||
|
return r_color
|
||||||
|
else:
|
||||||
|
def random_color():
|
||||||
|
while True:
|
||||||
|
r_color = np.random.rand(4)
|
||||||
|
r_color[3] = 1
|
||||||
|
|
||||||
|
new_color = '#'
|
||||||
|
for idx in range(len(r_color)):
|
||||||
|
new_color += '%x' % int(r_color[idx] * 255)
|
||||||
|
# do it until a valid color is generated
|
||||||
|
# a valid color has the # symbol, another 6 chars for the color and the last 2 chars for alpha
|
||||||
|
# for a total of 9 chars
|
||||||
|
if len(new_color) == 9:
|
||||||
|
break
|
||||||
|
return new_color
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# plot solid geometries found as members of self.tools attribute dict
|
# plot solid geometries found as members of self.tools attribute dict
|
||||||
# for MultiGeo
|
# for MultiGeo
|
||||||
@@ -2505,7 +2697,8 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
for tooluid_key in self.tools:
|
for tooluid_key in self.tools:
|
||||||
solid_geometry = self.tools[tooluid_key]['solid_geometry']
|
solid_geometry = self.tools[tooluid_key]['solid_geometry']
|
||||||
self.plot_element(solid_geometry, visible=visible,
|
self.plot_element(solid_geometry, visible=visible,
|
||||||
color=self.app.defaults["geometry_plot_line"])
|
color=random_color() if self.options['multicolored']
|
||||||
|
else self.app.defaults["geometry_plot_line"])
|
||||||
else:
|
else:
|
||||||
# plot solid geometry that may be an direct attribute of the geometry object
|
# plot solid geometry that may be an direct attribute of the geometry object
|
||||||
# for SingleGeo
|
# for SingleGeo
|
||||||
@@ -2573,218 +2766,11 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||||||
self.ui.plot_cb.setChecked(True)
|
self.ui.plot_cb.setChecked(True)
|
||||||
self.ui_connect()
|
self.ui_connect()
|
||||||
|
|
||||||
def on_add_area_click(self):
|
def on_multicolored_cb_click(self, *args):
|
||||||
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the start point of the area."))
|
if self.muted_ui:
|
||||||
self.app.call_source = 'geometry'
|
|
||||||
|
|
||||||
if self.app.is_legacy is False:
|
|
||||||
self.app.plotcanvas.graph_event_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
|
|
||||||
self.app.plotcanvas.graph_event_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
|
|
||||||
self.app.plotcanvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
|
|
||||||
else:
|
|
||||||
self.app.plotcanvas.graph_event_disconnect(self.app.mp)
|
|
||||||
self.app.plotcanvas.graph_event_disconnect(self.app.mm)
|
|
||||||
self.app.plotcanvas.graph_event_disconnect(self.app.mr)
|
|
||||||
|
|
||||||
self.mr = self.app.plotcanvas.graph_event_connect('mouse_release', self.on_mouse_release)
|
|
||||||
self.mm = self.app.plotcanvas.graph_event_connect('mouse_move', self.on_mouse_move)
|
|
||||||
# self.kp = self.app.plotcanvas.graph_event_connect('key_press', self.on_key_press)
|
|
||||||
|
|
||||||
# To be called after clicking on the plot.
|
|
||||||
def on_mouse_release(self, event):
|
|
||||||
if self.app.is_legacy is False:
|
|
||||||
event_pos = event.pos
|
|
||||||
# event_is_dragging = event.is_dragging
|
|
||||||
right_button = 2
|
|
||||||
else:
|
|
||||||
event_pos = (event.xdata, event.ydata)
|
|
||||||
# event_is_dragging = self.app.plotcanvas.is_dragging
|
|
||||||
right_button = 3
|
|
||||||
|
|
||||||
event_pos = self.app.plotcanvas.translate_coords(event_pos)
|
|
||||||
if self.app.grid_status():
|
|
||||||
curr_pos = self.app.geo_editor.snap(event_pos[0], event_pos[1])
|
|
||||||
else:
|
|
||||||
curr_pos = (event_pos[0], event_pos[1])
|
|
||||||
|
|
||||||
x1, y1 = curr_pos[0], curr_pos[1]
|
|
||||||
|
|
||||||
shape_type = self.ui.area_shape_radio.get_value()
|
|
||||||
|
|
||||||
# do clear area only for left mouse clicks
|
|
||||||
if event.button == 1:
|
|
||||||
if shape_type == "square":
|
|
||||||
if self.first_click is False:
|
|
||||||
self.first_click = True
|
|
||||||
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the end point of the area."))
|
|
||||||
|
|
||||||
self.cursor_pos = self.app.plotcanvas.translate_coords(event_pos)
|
|
||||||
if self.app.grid_status():
|
|
||||||
self.cursor_pos = self.app.geo_editor.snap(event_pos[0], event_pos[1])
|
|
||||||
else:
|
|
||||||
self.app.inform.emit(_("Zone added. Click to start adding next zone or right click to finish."))
|
|
||||||
self.app.delete_selection_shape()
|
|
||||||
|
|
||||||
x0, y0 = self.cursor_pos[0], self.cursor_pos[1]
|
|
||||||
|
|
||||||
pt1 = (x0, y0)
|
|
||||||
pt2 = (x1, y0)
|
|
||||||
pt3 = (x1, y1)
|
|
||||||
pt4 = (x0, y1)
|
|
||||||
|
|
||||||
new_rectangle = Polygon([pt1, pt2, pt3, pt4])
|
|
||||||
self.exclusion_areas_list.append(new_rectangle)
|
|
||||||
|
|
||||||
# add a temporary shape on canvas
|
|
||||||
FlatCAMTool.FlatCAMTool.draw_tool_selection_shape(self, old_coords=(x0, y0), coords=(x1, y1))
|
|
||||||
|
|
||||||
self.first_click = False
|
|
||||||
return
|
return
|
||||||
else:
|
self.read_form_item('multicolored')
|
||||||
self.points.append((x1, y1))
|
self.plot()
|
||||||
|
|
||||||
if len(self.points) > 1:
|
|
||||||
self.poly_drawn = True
|
|
||||||
self.app.inform.emit(_("Click on next Point or click right mouse button to complete ..."))
|
|
||||||
|
|
||||||
return ""
|
|
||||||
elif event.button == right_button and self.mouse_is_dragging is False:
|
|
||||||
|
|
||||||
shape_type = self.ui.area_shape_radio.get_value()
|
|
||||||
|
|
||||||
if shape_type == "square":
|
|
||||||
self.first_click = False
|
|
||||||
else:
|
|
||||||
# if we finish to add a polygon
|
|
||||||
if self.poly_drawn is True:
|
|
||||||
try:
|
|
||||||
# try to add the point where we last clicked if it is not already in the self.points
|
|
||||||
last_pt = (x1, y1)
|
|
||||||
if last_pt != self.points[-1]:
|
|
||||||
self.points.append(last_pt)
|
|
||||||
except IndexError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# we need to add a Polygon and a Polygon can be made only from at least 3 points
|
|
||||||
if len(self.points) > 2:
|
|
||||||
FlatCAMTool.FlatCAMTool.delete_moving_selection_shape(self)
|
|
||||||
pol = Polygon(self.points)
|
|
||||||
# do not add invalid polygons even if they are drawn by utility geometry
|
|
||||||
if pol.is_valid:
|
|
||||||
self.exclusion_areas_list.append(pol)
|
|
||||||
FlatCAMTool.FlatCAMTool.draw_selection_shape_polygon(self, points=self.points)
|
|
||||||
self.app.inform.emit(
|
|
||||||
_("Zone added. Click to start adding next zone or right click to finish."))
|
|
||||||
|
|
||||||
self.points = []
|
|
||||||
self.poly_drawn = False
|
|
||||||
return
|
|
||||||
|
|
||||||
FlatCAMTool.FlatCAMTool.delete_tool_selection_shape(self)
|
|
||||||
|
|
||||||
if self.app.is_legacy is False:
|
|
||||||
self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_release)
|
|
||||||
self.app.plotcanvas.graph_event_disconnect('mouse_move', self.on_mouse_move)
|
|
||||||
# self.app.plotcanvas.graph_event_disconnect('key_press', self.on_key_press)
|
|
||||||
else:
|
|
||||||
self.app.plotcanvas.graph_event_disconnect(self.mr)
|
|
||||||
self.app.plotcanvas.graph_event_disconnect(self.mm)
|
|
||||||
# self.app.plotcanvas.graph_event_disconnect(self.kp)
|
|
||||||
|
|
||||||
self.app.mp = self.app.plotcanvas.graph_event_connect('mouse_press',
|
|
||||||
self.app.on_mouse_click_over_plot)
|
|
||||||
self.app.mm = self.app.plotcanvas.graph_event_connect('mouse_move',
|
|
||||||
self.app.on_mouse_move_over_plot)
|
|
||||||
self.app.mr = self.app.plotcanvas.graph_event_connect('mouse_release',
|
|
||||||
self.app.on_mouse_click_release_over_plot)
|
|
||||||
|
|
||||||
self.app.call_source = 'app'
|
|
||||||
|
|
||||||
if len(self.exclusion_areas_list) == 0:
|
|
||||||
return
|
|
||||||
|
|
||||||
def area_disconnect(self):
|
|
||||||
if self.app.is_legacy is False:
|
|
||||||
self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_release)
|
|
||||||
self.app.plotcanvas.graph_event_disconnect('mouse_move', self.on_mouse_move)
|
|
||||||
else:
|
|
||||||
self.app.plotcanvas.graph_event_disconnect(self.mr)
|
|
||||||
self.app.plotcanvas.graph_event_disconnect(self.mm)
|
|
||||||
self.app.plotcanvas.graph_event_disconnect(self.kp)
|
|
||||||
|
|
||||||
self.app.mp = self.app.plotcanvas.graph_event_connect('mouse_press',
|
|
||||||
self.app.on_mouse_click_over_plot)
|
|
||||||
self.app.mm = self.app.plotcanvas.graph_event_connect('mouse_move',
|
|
||||||
self.app.on_mouse_move_over_plot)
|
|
||||||
self.app.mr = self.app.plotcanvas.graph_event_connect('mouse_release',
|
|
||||||
self.app.on_mouse_click_release_over_plot)
|
|
||||||
self.points = []
|
|
||||||
self.poly_drawn = False
|
|
||||||
self.exclusion_areas_list = []
|
|
||||||
|
|
||||||
FlatCAMTool.FlatCAMTool.delete_moving_selection_shape(self)
|
|
||||||
FlatCAMTool.FlatCAMTool.delete_tool_selection_shape(self)
|
|
||||||
|
|
||||||
self.app.call_source = "app"
|
|
||||||
self.app.inform.emit("[WARNING_NOTCL] %s" % _("Cancelled. Area exclusion drawing was interrupted."))
|
|
||||||
|
|
||||||
# called on mouse move
|
|
||||||
def on_mouse_move(self, event):
|
|
||||||
shape_type = self.ui.area_shape_radio.get_value()
|
|
||||||
|
|
||||||
if self.app.is_legacy is False:
|
|
||||||
event_pos = event.pos
|
|
||||||
event_is_dragging = event.is_dragging
|
|
||||||
# right_button = 2
|
|
||||||
else:
|
|
||||||
event_pos = (event.xdata, event.ydata)
|
|
||||||
event_is_dragging = self.app.plotcanvas.is_dragging
|
|
||||||
# right_button = 3
|
|
||||||
|
|
||||||
curr_pos = self.app.plotcanvas.translate_coords(event_pos)
|
|
||||||
|
|
||||||
# detect mouse dragging motion
|
|
||||||
if event_is_dragging is True:
|
|
||||||
self.mouse_is_dragging = True
|
|
||||||
else:
|
|
||||||
self.mouse_is_dragging = False
|
|
||||||
|
|
||||||
# update the cursor position
|
|
||||||
if self.app.grid_status():
|
|
||||||
# Update cursor
|
|
||||||
curr_pos = self.app.geo_editor.snap(curr_pos[0], curr_pos[1])
|
|
||||||
|
|
||||||
self.app.app_cursor.set_data(np.asarray([(curr_pos[0], curr_pos[1])]),
|
|
||||||
symbol='++', edge_color=self.app.cursor_color_3D,
|
|
||||||
edge_width=self.app.defaults["global_cursor_width"],
|
|
||||||
size=self.app.defaults["global_cursor_size"])
|
|
||||||
|
|
||||||
# update the positions on status bar
|
|
||||||
self.app.ui.position_label.setText(" <b>X</b>: %.4f "
|
|
||||||
"<b>Y</b>: %.4f" % (curr_pos[0], curr_pos[1]))
|
|
||||||
if self.cursor_pos is None:
|
|
||||||
self.cursor_pos = (0, 0)
|
|
||||||
|
|
||||||
self.app.dx = curr_pos[0] - float(self.cursor_pos[0])
|
|
||||||
self.app.dy = curr_pos[1] - float(self.cursor_pos[1])
|
|
||||||
self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f <b>Dy</b>: "
|
|
||||||
"%.4f " % (self.app.dx, self.app.dy))
|
|
||||||
|
|
||||||
# draw the utility geometry
|
|
||||||
if shape_type == "square":
|
|
||||||
if self.first_click:
|
|
||||||
self.app.delete_selection_shape()
|
|
||||||
self.app.draw_moving_selection_shape(old_coords=(self.cursor_pos[0], self.cursor_pos[1]),
|
|
||||||
coords=(curr_pos[0], curr_pos[1]))
|
|
||||||
else:
|
|
||||||
FlatCAMTool.FlatCAMTool.delete_moving_selection_shape(self)
|
|
||||||
FlatCAMTool.FlatCAMTool.draw_moving_selection_shape_poly(
|
|
||||||
self, points=self.points, data=(curr_pos[0], curr_pos[1]))
|
|
||||||
|
|
||||||
def on_clear_area_click(self):
|
|
||||||
self.exclusion_areas_list = []
|
|
||||||
FlatCAMTool.FlatCAMTool.delete_moving_selection_shape(self)
|
|
||||||
self.app.delete_selection_shape()
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def merge(geo_list, geo_final, multigeo=None):
|
def merge(geo_list, geo_final, multigeo=None):
|
||||||
@@ -14,15 +14,15 @@
|
|||||||
from shapely.geometry import Point, Polygon, MultiPolygon, MultiLineString, LineString, LinearRing
|
from shapely.geometry import Point, Polygon, MultiPolygon, MultiLineString, LineString, LinearRing
|
||||||
from shapely.ops import cascaded_union
|
from shapely.ops import cascaded_union
|
||||||
|
|
||||||
from flatcamParsers.ParseGerber import Gerber
|
from AppParsers.ParseGerber import Gerber
|
||||||
from flatcamObjects.FlatCAMObj import *
|
from AppObjects.FlatCAMObj import *
|
||||||
|
|
||||||
import math
|
import math
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -115,23 +115,12 @@ class GerberObject(FlatCAMObj, Gerber):
|
|||||||
"plot": True,
|
"plot": True,
|
||||||
"multicolored": False,
|
"multicolored": False,
|
||||||
"solid": False,
|
"solid": False,
|
||||||
"tool_type": 'circular',
|
|
||||||
"vtipdia": 0.1,
|
|
||||||
"vtipangle": 30,
|
|
||||||
"vcutz": -0.05,
|
|
||||||
"isotooldia": 0.016,
|
|
||||||
"isopasses": 1,
|
|
||||||
"isooverlap": 15,
|
|
||||||
"milling_type": "cl",
|
|
||||||
"combine_passes": True,
|
|
||||||
"noncoppermargin": 0.0,
|
"noncoppermargin": 0.0,
|
||||||
"noncopperrounded": False,
|
"noncopperrounded": False,
|
||||||
"bboxmargin": 0.0,
|
"bboxmargin": 0.0,
|
||||||
"bboxrounded": False,
|
"bboxrounded": False,
|
||||||
"aperture_display": False,
|
"aperture_display": False,
|
||||||
"follow": False,
|
"follow": False,
|
||||||
"iso_scope": 'all',
|
|
||||||
"iso_type": 'full'
|
|
||||||
})
|
})
|
||||||
|
|
||||||
# type of isolation: 0 = exteriors, 1 = interiors, 2 = complete isolation (both interiors and exteriors)
|
# type of isolation: 0 = exteriors, 1 = interiors, 2 = complete isolation (both interiors and exteriors)
|
||||||
@@ -197,88 +186,44 @@ class GerberObject(FlatCAMObj, Gerber):
|
|||||||
"plot": self.ui.plot_cb,
|
"plot": self.ui.plot_cb,
|
||||||
"multicolored": self.ui.multicolored_cb,
|
"multicolored": self.ui.multicolored_cb,
|
||||||
"solid": self.ui.solid_cb,
|
"solid": self.ui.solid_cb,
|
||||||
"tool_type": self.ui.tool_type_radio,
|
|
||||||
"vtipdia": self.ui.tipdia_spinner,
|
|
||||||
"vtipangle": self.ui.tipangle_spinner,
|
|
||||||
"vcutz": self.ui.cutz_spinner,
|
|
||||||
"isotooldia": self.ui.iso_tool_dia_entry,
|
|
||||||
"isopasses": self.ui.iso_width_entry,
|
|
||||||
"isooverlap": self.ui.iso_overlap_entry,
|
|
||||||
"milling_type": self.ui.milling_type_radio,
|
|
||||||
"combine_passes": self.ui.combine_passes_cb,
|
|
||||||
"noncoppermargin": self.ui.noncopper_margin_entry,
|
"noncoppermargin": self.ui.noncopper_margin_entry,
|
||||||
"noncopperrounded": self.ui.noncopper_rounded_cb,
|
"noncopperrounded": self.ui.noncopper_rounded_cb,
|
||||||
"bboxmargin": self.ui.bbmargin_entry,
|
"bboxmargin": self.ui.bbmargin_entry,
|
||||||
"bboxrounded": self.ui.bbrounded_cb,
|
"bboxrounded": self.ui.bbrounded_cb,
|
||||||
"aperture_display": self.ui.aperture_table_visibility_cb,
|
"aperture_display": self.ui.aperture_table_visibility_cb,
|
||||||
"follow": self.ui.follow_cb,
|
"follow": self.ui.follow_cb
|
||||||
"iso_scope": self.ui.iso_scope_radio,
|
|
||||||
"iso_type": self.ui.iso_type_radio
|
|
||||||
})
|
})
|
||||||
|
|
||||||
# Fill form fields only on object create
|
# Fill form fields only on object create
|
||||||
self.to_form()
|
self.to_form()
|
||||||
|
|
||||||
assert isinstance(self.ui, GerberObjectUI)
|
assert isinstance(self.ui, GerberObjectUI)
|
||||||
|
|
||||||
self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click)
|
self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click)
|
||||||
self.ui.solid_cb.stateChanged.connect(self.on_solid_cb_click)
|
self.ui.solid_cb.stateChanged.connect(self.on_solid_cb_click)
|
||||||
self.ui.multicolored_cb.stateChanged.connect(self.on_multicolored_cb_click)
|
self.ui.multicolored_cb.stateChanged.connect(self.on_multicolored_cb_click)
|
||||||
self.ui.generate_iso_button.clicked.connect(self.on_iso_button_click)
|
|
||||||
|
# Tools
|
||||||
|
self.ui.iso_button.clicked.connect(self.app.isolation_tool.run)
|
||||||
self.ui.generate_ncc_button.clicked.connect(self.app.ncclear_tool.run)
|
self.ui.generate_ncc_button.clicked.connect(self.app.ncclear_tool.run)
|
||||||
self.ui.generate_cutout_button.clicked.connect(self.app.cutout_tool.run)
|
self.ui.generate_cutout_button.clicked.connect(self.app.cutout_tool.run)
|
||||||
|
|
||||||
self.ui.generate_bb_button.clicked.connect(self.on_generatebb_button_click)
|
self.ui.generate_bb_button.clicked.connect(self.on_generatebb_button_click)
|
||||||
self.ui.generate_noncopper_button.clicked.connect(self.on_generatenoncopper_button_click)
|
self.ui.generate_noncopper_button.clicked.connect(self.on_generatenoncopper_button_click)
|
||||||
self.ui.aperture_table_visibility_cb.stateChanged.connect(self.on_aperture_table_visibility_change)
|
self.ui.aperture_table_visibility_cb.stateChanged.connect(self.on_aperture_table_visibility_change)
|
||||||
self.ui.follow_cb.stateChanged.connect(self.on_follow_cb_click)
|
self.ui.follow_cb.stateChanged.connect(self.on_follow_cb_click)
|
||||||
|
|
||||||
# set the model for the Area Exception comboboxes
|
|
||||||
self.ui.obj_combo.setModel(self.app.collection)
|
|
||||||
self.ui.obj_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
|
|
||||||
self.ui.obj_combo.is_last = True
|
|
||||||
self.ui.obj_combo.obj_type = {
|
|
||||||
_("Gerber"): "Gerber", _("Geometry"): "Geometry"
|
|
||||||
}[self.ui.type_obj_combo.get_value()]
|
|
||||||
self.on_type_obj_index_changed()
|
|
||||||
|
|
||||||
self.ui.type_obj_combo.currentIndexChanged.connect(self.on_type_obj_index_changed)
|
|
||||||
|
|
||||||
self.ui.tool_type_radio.activated_custom.connect(self.on_tool_type_change)
|
|
||||||
# establish visibility for the GUI elements found in the slot function
|
|
||||||
self.ui.tool_type_radio.activated_custom.emit(self.options['tool_type'])
|
|
||||||
|
|
||||||
# Show/Hide Advanced Options
|
# Show/Hide Advanced Options
|
||||||
if self.app.defaults["global_app_level"] == 'b':
|
if self.app.defaults["global_app_level"] == 'b':
|
||||||
self.ui.level.setText('<span style="color:green;"><b>%s</b></span>' % _('Basic'))
|
self.ui.level.setText('<span style="color:green;"><b>%s</b></span>' % _('Basic'))
|
||||||
self.options['tool_type'] = 'circular'
|
|
||||||
|
|
||||||
self.ui.tool_type_label.hide()
|
|
||||||
self.ui.tool_type_radio.hide()
|
|
||||||
|
|
||||||
# override the Preferences Value; in Basic mode the Tool Type is always Circular ('C1')
|
|
||||||
self.ui.tool_type_radio.set_value('circular')
|
|
||||||
|
|
||||||
self.ui.tipdialabel.hide()
|
|
||||||
self.ui.tipdia_spinner.hide()
|
|
||||||
self.ui.tipanglelabel.hide()
|
|
||||||
self.ui.tipangle_spinner.hide()
|
|
||||||
self.ui.cutzlabel.hide()
|
|
||||||
self.ui.cutz_spinner.hide()
|
|
||||||
|
|
||||||
self.ui.apertures_table_label.hide()
|
self.ui.apertures_table_label.hide()
|
||||||
self.ui.aperture_table_visibility_cb.hide()
|
self.ui.aperture_table_visibility_cb.hide()
|
||||||
self.ui.milling_type_label.hide()
|
|
||||||
self.ui.milling_type_radio.hide()
|
|
||||||
self.ui.iso_type_label.hide()
|
|
||||||
self.ui.iso_type_radio.hide()
|
|
||||||
|
|
||||||
self.ui.follow_cb.hide()
|
self.ui.follow_cb.hide()
|
||||||
self.ui.except_cb.setChecked(False)
|
|
||||||
self.ui.except_cb.hide()
|
|
||||||
else:
|
else:
|
||||||
self.ui.level.setText('<span style="color:red;"><b>%s</b></span>' % _('Advanced'))
|
self.ui.level.setText('<span style="color:red;"><b>%s</b></span>' % _('Advanced'))
|
||||||
self.ui.tipdia_spinner.valueChanged.connect(self.on_calculate_tooldia)
|
|
||||||
self.ui.tipangle_spinner.valueChanged.connect(self.on_calculate_tooldia)
|
|
||||||
self.ui.cutz_spinner.valueChanged.connect(self.on_calculate_tooldia)
|
|
||||||
|
|
||||||
if self.app.defaults["gerber_buffering"] == 'no':
|
if self.app.defaults["gerber_buffering"] == 'no':
|
||||||
self.ui.create_buffer_button.show()
|
self.ui.create_buffer_button.show()
|
||||||
@@ -296,58 +241,6 @@ class GerberObject(FlatCAMObj, Gerber):
|
|||||||
self.build_ui()
|
self.build_ui()
|
||||||
self.units_found = self.app.defaults['units']
|
self.units_found = self.app.defaults['units']
|
||||||
|
|
||||||
def on_calculate_tooldia(self):
|
|
||||||
try:
|
|
||||||
tdia = float(self.ui.tipdia_spinner.get_value())
|
|
||||||
except Exception:
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
dang = float(self.ui.tipangle_spinner.get_value())
|
|
||||||
except Exception:
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
cutz = float(self.ui.cutz_spinner.get_value())
|
|
||||||
except Exception:
|
|
||||||
return
|
|
||||||
|
|
||||||
cutz *= -1
|
|
||||||
if cutz < 0:
|
|
||||||
cutz *= -1
|
|
||||||
|
|
||||||
half_tip_angle = dang / 2
|
|
||||||
|
|
||||||
tool_diameter = tdia + (2 * cutz * math.tan(math.radians(half_tip_angle)))
|
|
||||||
self.ui.iso_tool_dia_entry.set_value(tool_diameter)
|
|
||||||
|
|
||||||
def on_type_obj_index_changed(self):
|
|
||||||
val = self.ui.type_obj_combo.get_value()
|
|
||||||
obj_type = {"Gerber": 0, "Geometry": 2}[val]
|
|
||||||
self.ui.obj_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
|
|
||||||
self.ui.obj_combo.setCurrentIndex(0)
|
|
||||||
self.ui.obj_combo.obj_type = {_("Gerber"): "Gerber", _("Geometry"): "Geometry"}[val]
|
|
||||||
|
|
||||||
def on_tool_type_change(self, state):
|
|
||||||
if state == 'circular':
|
|
||||||
self.ui.tipdialabel.hide()
|
|
||||||
self.ui.tipdia_spinner.hide()
|
|
||||||
self.ui.tipanglelabel.hide()
|
|
||||||
self.ui.tipangle_spinner.hide()
|
|
||||||
self.ui.cutzlabel.hide()
|
|
||||||
self.ui.cutz_spinner.hide()
|
|
||||||
self.ui.iso_tool_dia_entry.setDisabled(False)
|
|
||||||
# update the value in the self.iso_tool_dia_entry once this is selected
|
|
||||||
self.ui.iso_tool_dia_entry.set_value(self.options['isotooldia'])
|
|
||||||
else:
|
|
||||||
self.ui.tipdialabel.show()
|
|
||||||
self.ui.tipdia_spinner.show()
|
|
||||||
self.ui.tipanglelabel.show()
|
|
||||||
self.ui.tipangle_spinner.show()
|
|
||||||
self.ui.cutzlabel.show()
|
|
||||||
self.ui.cutz_spinner.show()
|
|
||||||
self.ui.iso_tool_dia_entry.setDisabled(True)
|
|
||||||
# update the value in the self.iso_tool_dia_entry once this is selected
|
|
||||||
self.on_calculate_tooldia()
|
|
||||||
|
|
||||||
def build_ui(self):
|
def build_ui(self):
|
||||||
FlatCAMObj.build_ui(self)
|
FlatCAMObj.build_ui(self)
|
||||||
|
|
||||||
@@ -530,7 +423,7 @@ class GerberObject(FlatCAMObj, Gerber):
|
|||||||
return "fail"
|
return "fail"
|
||||||
geo_obj.solid_geometry = non_copper
|
geo_obj.solid_geometry = non_copper
|
||||||
|
|
||||||
self.app.new_object("geometry", name, geo_init)
|
self.app.app_obj.new_object("geometry", name, geo_init)
|
||||||
|
|
||||||
def on_generatebb_button_click(self, *args):
|
def on_generatebb_button_click(self, *args):
|
||||||
self.app.defaults.report_usage("gerber_on_generatebb_button")
|
self.app.defaults.report_usage("gerber_on_generatebb_button")
|
||||||
@@ -556,523 +449,7 @@ class GerberObject(FlatCAMObj, Gerber):
|
|||||||
return "fail"
|
return "fail"
|
||||||
geo_obj.solid_geometry = bounding_box
|
geo_obj.solid_geometry = bounding_box
|
||||||
|
|
||||||
self.app.new_object("geometry", name, geo_init)
|
self.app.app_obj.new_object("geometry", name, geo_init)
|
||||||
|
|
||||||
def on_iso_button_click(self, *args):
|
|
||||||
|
|
||||||
obj = self.app.collection.get_active()
|
|
||||||
|
|
||||||
self.iso_type = 2
|
|
||||||
if self.ui.iso_type_radio.get_value() == 'ext':
|
|
||||||
self.iso_type = 0
|
|
||||||
if self.ui.iso_type_radio.get_value() == 'int':
|
|
||||||
self.iso_type = 1
|
|
||||||
|
|
||||||
def worker_task(iso_obj, app_obj):
|
|
||||||
with self.app.proc_container.new(_("Isolating...")):
|
|
||||||
if self.ui.follow_cb.get_value() is True:
|
|
||||||
iso_obj.follow_geo()
|
|
||||||
# in the end toggle the visibility of the origin object so we can see the generated Geometry
|
|
||||||
iso_obj.ui.plot_cb.toggle()
|
|
||||||
else:
|
|
||||||
app_obj.defaults.report_usage("gerber_on_iso_button")
|
|
||||||
self.read_form()
|
|
||||||
|
|
||||||
iso_scope = 'all' if self.ui.iso_scope_radio.get_value() == 'all' else 'single'
|
|
||||||
self.isolate_handler(iso_type=self.iso_type, iso_scope=iso_scope)
|
|
||||||
|
|
||||||
self.app.worker_task.emit({'fcn': worker_task, 'params': [obj, self.app]})
|
|
||||||
|
|
||||||
def follow_geo(self, outname=None):
|
|
||||||
"""
|
|
||||||
Creates a geometry object "following" the gerber paths.
|
|
||||||
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
|
|
||||||
# default_name = self.options["name"] + "_follow"
|
|
||||||
# follow_name = outname or default_name
|
|
||||||
|
|
||||||
if outname is None:
|
|
||||||
follow_name = self.options["name"] + "_follow"
|
|
||||||
else:
|
|
||||||
follow_name = outname
|
|
||||||
|
|
||||||
def follow_init(follow_obj, app):
|
|
||||||
# Propagate options
|
|
||||||
follow_obj.options["cnctooldia"] = str(self.options["isotooldia"])
|
|
||||||
follow_obj.solid_geometry = self.follow_geometry
|
|
||||||
|
|
||||||
# TODO: Do something if this is None. Offer changing name?
|
|
||||||
try:
|
|
||||||
self.app.new_object("geometry", follow_name, follow_init)
|
|
||||||
except Exception as e:
|
|
||||||
return "Operation failed: %s" % str(e)
|
|
||||||
|
|
||||||
def isolate_handler(self, iso_type, iso_scope):
|
|
||||||
|
|
||||||
if iso_scope == 'all':
|
|
||||||
self.isolate(iso_type=iso_type)
|
|
||||||
else:
|
|
||||||
# disengage the grid snapping since it may be hard to click on polygons with grid snapping on
|
|
||||||
if self.app.ui.grid_snap_btn.isChecked():
|
|
||||||
self.grid_status_memory = True
|
|
||||||
self.app.ui.grid_snap_btn.trigger()
|
|
||||||
else:
|
|
||||||
self.grid_status_memory = False
|
|
||||||
|
|
||||||
self.mr = self.app.plotcanvas.graph_event_connect('mouse_release', self.on_mouse_click_release)
|
|
||||||
|
|
||||||
if self.app.is_legacy is False:
|
|
||||||
self.app.plotcanvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
|
|
||||||
else:
|
|
||||||
self.app.plotcanvas.graph_event_disconnect(self.app.mr)
|
|
||||||
|
|
||||||
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click on a polygon to isolate it."))
|
|
||||||
|
|
||||||
def on_mouse_click_release(self, event):
|
|
||||||
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
|
|
||||||
|
|
||||||
try:
|
|
||||||
x = float(event_pos[0])
|
|
||||||
y = float(event_pos[1])
|
|
||||||
except TypeError:
|
|
||||||
return
|
|
||||||
|
|
||||||
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 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',
|
|
||||||
face_color=self.app.defaults['global_sel_draw_color'] + 'AF',
|
|
||||||
visible=True)
|
|
||||||
self.poly_dict[shape_id] = clicked_poly
|
|
||||||
self.app.inform.emit(
|
|
||||||
'%s: %d. %s' % (_("Added polygon"), int(len(self.poly_dict)),
|
|
||||||
_("Click to add next polygon or right click to start isolation."))
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
for k, v in list(self.poly_dict.items()):
|
|
||||||
if v == clicked_poly:
|
|
||||||
self.app.tool_shapes.remove(k)
|
|
||||||
self.poly_dict.pop(k)
|
|
||||||
break
|
|
||||||
except TypeError:
|
|
||||||
return
|
|
||||||
self.app.inform.emit(
|
|
||||||
'%s. %s' % (_("Removed polygon"),
|
|
||||||
_("Click to add/remove next polygon or right click to start isolation."))
|
|
||||||
)
|
|
||||||
|
|
||||||
self.app.tool_shapes.redraw()
|
|
||||||
else:
|
|
||||||
self.app.inform.emit(_("No polygon detected under click position."))
|
|
||||||
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()
|
|
||||||
|
|
||||||
if self.app.is_legacy is False:
|
|
||||||
self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_click_release)
|
|
||||||
else:
|
|
||||||
self.app.plotcanvas.graph_event_disconnect(self.mr)
|
|
||||||
|
|
||||||
self.app.mr = self.app.plotcanvas.graph_event_connect('mouse_release',
|
|
||||||
self.app.on_mouse_click_release_over_plot)
|
|
||||||
|
|
||||||
self.app.tool_shapes.clear(update=True)
|
|
||||||
|
|
||||||
if self.poly_dict:
|
|
||||||
poly_list = deepcopy(list(self.poly_dict.values()))
|
|
||||||
self.isolate(iso_type=self.iso_type, geometry=poly_list)
|
|
||||||
self.poly_dict.clear()
|
|
||||||
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):
|
|
||||||
"""
|
|
||||||
Creates an isolation routing geometry object in the project.
|
|
||||||
|
|
||||||
:param iso_type: type of isolation to be done: 0 = exteriors, 1 = interiors and 2 = both
|
|
||||||
:param geometry: specific geometry to isolate
|
|
||||||
:param dia: Tool diameter
|
|
||||||
:param passes: Number of tool widths to cut
|
|
||||||
:param overlap: Overlap between passes in fraction of tool diameter
|
|
||||||
:param outname: Base name of the output object
|
|
||||||
:param combine: Boolean: if to combine passes in one resulting object in case of multiple passes
|
|
||||||
:param milling_type: type of milling: conventional or climbing
|
|
||||||
:param follow: Boolean: if to generate a 'follow' geometry
|
|
||||||
:param plot: Boolean: if to plot the resulting geometry object
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
|
|
||||||
if geometry is None:
|
|
||||||
work_geo = self.follow_geometry if follow is True else self.solid_geometry
|
|
||||||
else:
|
|
||||||
work_geo = geometry
|
|
||||||
|
|
||||||
if dia is None:
|
|
||||||
dia = float(self.options["isotooldia"])
|
|
||||||
|
|
||||||
if passes is None:
|
|
||||||
passes = int(self.options["isopasses"])
|
|
||||||
|
|
||||||
if overlap is None:
|
|
||||||
overlap = float(self.options["isooverlap"])
|
|
||||||
|
|
||||||
overlap /= 100.0
|
|
||||||
|
|
||||||
combine = self.options["combine_passes"] if combine is None else bool(combine)
|
|
||||||
|
|
||||||
if milling_type is None:
|
|
||||||
milling_type = self.options["milling_type"]
|
|
||||||
|
|
||||||
if iso_type is None:
|
|
||||||
iso_t = 2
|
|
||||||
else:
|
|
||||||
iso_t = iso_type
|
|
||||||
|
|
||||||
base_name = self.options["name"]
|
|
||||||
|
|
||||||
if combine:
|
|
||||||
if outname is None:
|
|
||||||
if self.iso_type == 0:
|
|
||||||
iso_name = base_name + "_ext_iso"
|
|
||||||
elif self.iso_type == 1:
|
|
||||||
iso_name = base_name + "_int_iso"
|
|
||||||
else:
|
|
||||||
iso_name = base_name + "_iso"
|
|
||||||
else:
|
|
||||||
iso_name = outname
|
|
||||||
|
|
||||||
def iso_init(geo_obj, app_obj):
|
|
||||||
# Propagate options
|
|
||||||
geo_obj.options["cnctooldia"] = str(self.options["isotooldia"])
|
|
||||||
geo_obj.tool_type = self.ui.tool_type_radio.get_value().upper()
|
|
||||||
|
|
||||||
geo_obj.solid_geometry = []
|
|
||||||
|
|
||||||
# transfer the Cut Z and Vtip and VAngle values in case that we use the V-Shape tool in Gerber UI
|
|
||||||
if self.ui.tool_type_radio.get_value() == 'v':
|
|
||||||
new_cutz = self.ui.cutz_spinner.get_value()
|
|
||||||
new_vtipdia = self.ui.tipdia_spinner.get_value()
|
|
||||||
new_vtipangle = self.ui.tipangle_spinner.get_value()
|
|
||||||
tool_type = 'V'
|
|
||||||
else:
|
|
||||||
new_cutz = self.app.defaults['geometry_cutz']
|
|
||||||
new_vtipdia = self.app.defaults['geometry_vtipdia']
|
|
||||||
new_vtipangle = self.app.defaults['geometry_vtipangle']
|
|
||||||
tool_type = 'C1'
|
|
||||||
|
|
||||||
# store here the default data for Geometry Data
|
|
||||||
default_data = {}
|
|
||||||
default_data.update({
|
|
||||||
"name": iso_name,
|
|
||||||
"plot": self.app.defaults['geometry_plot'],
|
|
||||||
"cutz": new_cutz,
|
|
||||||
"vtipdia": new_vtipdia,
|
|
||||||
"vtipangle": new_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']
|
|
||||||
})
|
|
||||||
|
|
||||||
geo_obj.tools = {}
|
|
||||||
geo_obj.tools['1'] = {}
|
|
||||||
geo_obj.tools.update({
|
|
||||||
'1': {
|
|
||||||
'tooldia': float(self.options["isotooldia"]),
|
|
||||||
'offset': 'Path',
|
|
||||||
'offset_value': 0.0,
|
|
||||||
'type': _('Rough'),
|
|
||||||
'tool_type': tool_type,
|
|
||||||
'data': default_data,
|
|
||||||
'solid_geometry': geo_obj.solid_geometry
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
for nr_pass in range(passes):
|
|
||||||
iso_offset = dia * ((2 * nr_pass + 1) / 2.0) - (nr_pass * overlap * dia)
|
|
||||||
|
|
||||||
# if milling type is climb then the move is counter-clockwise around features
|
|
||||||
mill_dir = 1 if milling_type == 'cl' else 0
|
|
||||||
geom = self.generate_envelope(iso_offset, mill_dir, geometry=work_geo, env_iso_type=iso_t,
|
|
||||||
follow=follow, nr_passes=nr_pass)
|
|
||||||
|
|
||||||
if geom == 'fail':
|
|
||||||
app_obj.inform.emit('[ERROR_NOTCL] %s' % _("Isolation geometry could not be generated."))
|
|
||||||
return 'fail'
|
|
||||||
geo_obj.solid_geometry.append(geom)
|
|
||||||
|
|
||||||
# update the geometry in the tools
|
|
||||||
geo_obj.tools['1']['solid_geometry'] = geo_obj.solid_geometry
|
|
||||||
|
|
||||||
# detect if solid_geometry is empty and this require list flattening which is "heavy"
|
|
||||||
# or just looking in the lists (they are one level depth) and if any is not empty
|
|
||||||
# proceed with object creation, if there are empty and the number of them is the length
|
|
||||||
# of the list then we have an empty solid_geometry which should raise a Custom Exception
|
|
||||||
empty_cnt = 0
|
|
||||||
if not isinstance(geo_obj.solid_geometry, list) and \
|
|
||||||
not isinstance(geo_obj.solid_geometry, MultiPolygon):
|
|
||||||
geo_obj.solid_geometry = [geo_obj.solid_geometry]
|
|
||||||
|
|
||||||
for g in geo_obj.solid_geometry:
|
|
||||||
if g:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
empty_cnt += 1
|
|
||||||
|
|
||||||
if empty_cnt == len(geo_obj.solid_geometry):
|
|
||||||
raise ValidationError("Empty Geometry", None)
|
|
||||||
else:
|
|
||||||
app_obj.inform.emit('[success] %s" %s' % (_("Isolation geometry created"), geo_obj.options["name"]))
|
|
||||||
|
|
||||||
# even if combine is checked, one pass is still single-geo
|
|
||||||
geo_obj.multigeo = True if passes > 1 else False
|
|
||||||
|
|
||||||
# ############################################################
|
|
||||||
# ########## AREA SUBTRACTION ################################
|
|
||||||
# ############################################################
|
|
||||||
if self.ui.except_cb.get_value():
|
|
||||||
self.app.proc_container.update_view_text(' %s' % _("Subtracting Geo"))
|
|
||||||
geo_obj.solid_geometry = self.area_subtraction(geo_obj.solid_geometry)
|
|
||||||
|
|
||||||
# TODO: Do something if this is None. Offer changing name?
|
|
||||||
self.app.new_object("geometry", iso_name, iso_init, plot=plot)
|
|
||||||
else:
|
|
||||||
for i in range(passes):
|
|
||||||
|
|
||||||
offset = dia * ((2 * i + 1) / 2.0) - (i * overlap * dia)
|
|
||||||
if passes > 1:
|
|
||||||
if outname is None:
|
|
||||||
if self.iso_type == 0:
|
|
||||||
iso_name = base_name + "_ext_iso" + str(i + 1)
|
|
||||||
elif self.iso_type == 1:
|
|
||||||
iso_name = base_name + "_int_iso" + str(i + 1)
|
|
||||||
else:
|
|
||||||
iso_name = base_name + "_iso" + str(i + 1)
|
|
||||||
else:
|
|
||||||
iso_name = outname
|
|
||||||
else:
|
|
||||||
if outname is None:
|
|
||||||
if self.iso_type == 0:
|
|
||||||
iso_name = base_name + "_ext_iso"
|
|
||||||
elif self.iso_type == 1:
|
|
||||||
iso_name = base_name + "_int_iso"
|
|
||||||
else:
|
|
||||||
iso_name = base_name + "_iso"
|
|
||||||
else:
|
|
||||||
iso_name = outname
|
|
||||||
|
|
||||||
def iso_init(geo_obj, app_obj):
|
|
||||||
# Propagate options
|
|
||||||
geo_obj.options["cnctooldia"] = str(self.options["isotooldia"])
|
|
||||||
if self.ui.tool_type_radio.get_value() == 'v':
|
|
||||||
geo_obj.tool_type = 'V'
|
|
||||||
else:
|
|
||||||
geo_obj.tool_type = 'C1'
|
|
||||||
|
|
||||||
# if milling type is climb then the move is counter-clockwise around features
|
|
||||||
mill_dir = 1 if milling_type == 'cl' else 0
|
|
||||||
geom = self.generate_envelope(offset, mill_dir, geometry=work_geo, env_iso_type=iso_t,
|
|
||||||
follow=follow,
|
|
||||||
nr_passes=i)
|
|
||||||
|
|
||||||
if geom == 'fail':
|
|
||||||
app_obj.inform.emit('[ERROR_NOTCL] %s' % _("Isolation geometry could not be generated."))
|
|
||||||
return 'fail'
|
|
||||||
|
|
||||||
geo_obj.solid_geometry = geom
|
|
||||||
|
|
||||||
# transfer the Cut Z and Vtip and VAngle values in case that we use the V-Shape tool in Gerber UI
|
|
||||||
# even if the resulting geometry is not multigeo we add the tools dict which will hold the data
|
|
||||||
# required to be transfered to the Geometry object
|
|
||||||
if self.ui.tool_type_radio.get_value() == 'v':
|
|
||||||
new_cutz = self.ui.cutz_spinner.get_value()
|
|
||||||
new_vtipdia = self.ui.tipdia_spinner.get_value()
|
|
||||||
new_vtipangle = self.ui.tipangle_spinner.get_value()
|
|
||||||
tool_type = 'V'
|
|
||||||
else:
|
|
||||||
new_cutz = self.app.defaults['geometry_cutz']
|
|
||||||
new_vtipdia = self.app.defaults['geometry_vtipdia']
|
|
||||||
new_vtipangle = self.app.defaults['geometry_vtipangle']
|
|
||||||
tool_type = 'C1'
|
|
||||||
|
|
||||||
# store here the default data for Geometry Data
|
|
||||||
default_data = {}
|
|
||||||
default_data.update({
|
|
||||||
"name": iso_name,
|
|
||||||
"plot": self.app.defaults['geometry_plot'],
|
|
||||||
"cutz": new_cutz,
|
|
||||||
"vtipdia": new_vtipdia,
|
|
||||||
"vtipangle": new_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']
|
|
||||||
})
|
|
||||||
|
|
||||||
geo_obj.tools = {}
|
|
||||||
geo_obj.tools['1'] = {}
|
|
||||||
geo_obj.tools.update({
|
|
||||||
'1': {
|
|
||||||
'tooldia': float(self.options["isotooldia"]),
|
|
||||||
'offset': 'Path',
|
|
||||||
'offset_value': 0.0,
|
|
||||||
'type': _('Rough'),
|
|
||||||
'tool_type': tool_type,
|
|
||||||
'data': default_data,
|
|
||||||
'solid_geometry': geo_obj.solid_geometry
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
# detect if solid_geometry is empty and this require list flattening which is "heavy"
|
|
||||||
# or just looking in the lists (they are one level depth) and if any is not empty
|
|
||||||
# proceed with object creation, if there are empty and the number of them is the length
|
|
||||||
# of the list then we have an empty solid_geometry which should raise a Custom Exception
|
|
||||||
empty_cnt = 0
|
|
||||||
if not isinstance(geo_obj.solid_geometry, list):
|
|
||||||
geo_obj.solid_geometry = [geo_obj.solid_geometry]
|
|
||||||
|
|
||||||
for g in geo_obj.solid_geometry:
|
|
||||||
if g:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
empty_cnt += 1
|
|
||||||
|
|
||||||
if empty_cnt == len(geo_obj.solid_geometry):
|
|
||||||
raise ValidationError("Empty Geometry", None)
|
|
||||||
else:
|
|
||||||
app_obj.inform.emit('[success] %s: %s' %
|
|
||||||
(_("Isolation geometry created"), geo_obj.options["name"]))
|
|
||||||
geo_obj.multigeo = False
|
|
||||||
|
|
||||||
# ############################################################
|
|
||||||
# ########## AREA SUBTRACTION ################################
|
|
||||||
# ############################################################
|
|
||||||
if self.ui.except_cb.get_value():
|
|
||||||
self.app.proc_container.update_view_text(' %s' % _("Subtracting Geo"))
|
|
||||||
geo_obj.solid_geometry = self.area_subtraction(geo_obj.solid_geometry)
|
|
||||||
|
|
||||||
# TODO: Do something if this is None. Offer changing name?
|
|
||||||
self.app.new_object("geometry", iso_name, iso_init, plot=plot)
|
|
||||||
|
|
||||||
def generate_envelope(self, offset, invert, geometry=None, env_iso_type=2, follow=None, nr_passes=0):
|
def generate_envelope(self, offset, invert, geometry=None, env_iso_type=2, follow=None, nr_passes=0):
|
||||||
# isolation_geometry produces an envelope that is going on the left of the geometry
|
# isolation_geometry produces an envelope that is going on the left of the geometry
|
||||||
@@ -1114,64 +491,28 @@ class GerberObject(FlatCAMObj, Gerber):
|
|||||||
return 'fail'
|
return 'fail'
|
||||||
return geom
|
return geom
|
||||||
|
|
||||||
def area_subtraction(self, geo, subtractor_geo=None):
|
def follow_geo(self, outname=None):
|
||||||
"""
|
"""
|
||||||
Subtracts the subtractor_geo (if present else self.solid_geometry) from the geo
|
Creates a geometry object "following" the gerber paths.
|
||||||
|
|
||||||
:param geo: target geometry from which to subtract
|
:return: None
|
||||||
:param subtractor_geo: geometry that acts as subtractor
|
|
||||||
:return:
|
|
||||||
"""
|
"""
|
||||||
new_geometry = []
|
|
||||||
target_geo = geo
|
|
||||||
|
|
||||||
if subtractor_geo:
|
if outname is None:
|
||||||
sub_union = cascaded_union(subtractor_geo)
|
follow_name = self.options["name"] + "_follow"
|
||||||
else:
|
else:
|
||||||
name = self.ui.obj_combo.currentText()
|
follow_name = outname
|
||||||
subtractor_obj = self.app.collection.get_by_name(name)
|
|
||||||
sub_union = cascaded_union(subtractor_obj.solid_geometry)
|
|
||||||
|
|
||||||
|
def follow_init(follow_obj, app):
|
||||||
|
# Propagate options
|
||||||
|
follow_obj.options["cnctooldia"] = str(self.options["isotooldia"])
|
||||||
|
follow_obj.solid_geometry = self.follow_geometry
|
||||||
|
|
||||||
|
# TODO: Do something if this is None. Offer changing name?
|
||||||
try:
|
try:
|
||||||
for geo_elem in target_geo:
|
self.app.app_obj.new_object("geometry", follow_name, follow_init)
|
||||||
if isinstance(geo_elem, Polygon):
|
except Exception as e:
|
||||||
for ring in self.poly2rings(geo_elem):
|
return "Operation failed: %s" % str(e)
|
||||||
new_geo = ring.difference(sub_union)
|
|
||||||
if new_geo and not new_geo.is_empty:
|
|
||||||
new_geometry.append(new_geo)
|
|
||||||
elif isinstance(geo_elem, MultiPolygon):
|
|
||||||
for poly in geo_elem:
|
|
||||||
for ring in self.poly2rings(poly):
|
|
||||||
new_geo = ring.difference(sub_union)
|
|
||||||
if new_geo and not new_geo.is_empty:
|
|
||||||
new_geometry.append(new_geo)
|
|
||||||
elif isinstance(geo_elem, LineString):
|
|
||||||
new_geo = geo_elem.difference(sub_union)
|
|
||||||
if new_geo:
|
|
||||||
if not new_geo.is_empty:
|
|
||||||
new_geometry.append(new_geo)
|
|
||||||
elif isinstance(geo_elem, MultiLineString):
|
|
||||||
for line_elem in geo_elem:
|
|
||||||
new_geo = line_elem.difference(sub_union)
|
|
||||||
if new_geo and not new_geo.is_empty:
|
|
||||||
new_geometry.append(new_geo)
|
|
||||||
except TypeError:
|
|
||||||
if isinstance(target_geo, Polygon):
|
|
||||||
for ring in self.poly2rings(target_geo):
|
|
||||||
new_geo = ring.difference(sub_union)
|
|
||||||
if new_geo:
|
|
||||||
if not new_geo.is_empty:
|
|
||||||
new_geometry.append(new_geo)
|
|
||||||
elif isinstance(target_geo, LineString):
|
|
||||||
new_geo = target_geo.difference(sub_union)
|
|
||||||
if new_geo and not new_geo.is_empty:
|
|
||||||
new_geometry.append(new_geo)
|
|
||||||
elif isinstance(target_geo, MultiLineString):
|
|
||||||
for line_elem in target_geo:
|
|
||||||
new_geo = line_elem.difference(sub_union)
|
|
||||||
if new_geo and not new_geo.is_empty:
|
|
||||||
new_geometry.append(new_geo)
|
|
||||||
return new_geometry
|
|
||||||
|
|
||||||
def on_plot_cb_click(self, *args):
|
def on_plot_cb_click(self, *args):
|
||||||
if self.muted_ui:
|
if self.muted_ui:
|
||||||
@@ -12,15 +12,15 @@
|
|||||||
|
|
||||||
import inspect # TODO: For debugging only.
|
import inspect # TODO: For debugging only.
|
||||||
|
|
||||||
from flatcamGUI.ObjectUI import *
|
from AppGUI.ObjectUI import *
|
||||||
|
|
||||||
from FlatCAMCommon import LoudDict
|
from Common import LoudDict
|
||||||
from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy
|
from AppGUI.PlotCanvasLegacy import ShapeCollectionLegacy
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -43,7 +43,7 @@ class ValidationError(Exception):
|
|||||||
class FlatCAMObj(QtCore.QObject):
|
class FlatCAMObj(QtCore.QObject):
|
||||||
"""
|
"""
|
||||||
Base type of objects handled in FlatCAM. These become interactive
|
Base type of objects handled in FlatCAM. These become interactive
|
||||||
in the GUI, can be plotted, and their options can be modified
|
in the AppGUI, can be plotted, and their options can be modified
|
||||||
by the user in their respective forms.
|
by the user in their respective forms.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -186,11 +186,9 @@ class FlatCAMObj(QtCore.QObject):
|
|||||||
|
|
||||||
def build_ui(self):
|
def build_ui(self):
|
||||||
"""
|
"""
|
||||||
Sets up the UI/form for this object. Show the UI
|
Sets up the UI/form for this object. Show the UI in the App.
|
||||||
in the App.
|
|
||||||
|
|
||||||
:return: None
|
:return: None
|
||||||
:rtype: None
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.muted_ui = True
|
self.muted_ui = True
|
||||||
@@ -247,14 +245,14 @@ class FlatCAMObj(QtCore.QObject):
|
|||||||
self.app.proc_container.update_view_text('')
|
self.app.proc_container.update_view_text('')
|
||||||
with self.app.proc_container.new('%s...' % _("Plotting")):
|
with self.app.proc_container.new('%s...' % _("Plotting")):
|
||||||
self.plot()
|
self.plot()
|
||||||
self.app.object_changed.emit(self)
|
self.app.app_obj.object_changed.emit(self)
|
||||||
|
|
||||||
self.app.worker_task.emit({'fcn': worker_task, 'params': []})
|
self.app.worker_task.emit({'fcn': worker_task, 'params': []})
|
||||||
|
|
||||||
def on_scale_button_click(self):
|
def on_scale_button_click(self):
|
||||||
self.read_form()
|
self.read_form()
|
||||||
try:
|
try:
|
||||||
factor = float(eval(self.ui.scale_entry.get_value()))
|
factor = float(self.ui.scale_entry.get_value())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _("Scaling could not be executed."))
|
self.app.inform.emit('[ERROR_NOTCL] %s' % _("Scaling could not be executed."))
|
||||||
log.debug("FlatCAMObj.on_scale_button_click() -- %s" % str(e))
|
log.debug("FlatCAMObj.on_scale_button_click() -- %s" % str(e))
|
||||||
@@ -278,7 +276,7 @@ class FlatCAMObj(QtCore.QObject):
|
|||||||
self.app.proc_container.update_view_text('')
|
self.app.proc_container.update_view_text('')
|
||||||
with self.app.proc_container.new('%s...' % _("Plotting")):
|
with self.app.proc_container.new('%s...' % _("Plotting")):
|
||||||
self.plot()
|
self.plot()
|
||||||
self.app.object_changed.emit(self)
|
self.app.app_obj.object_changed.emit(self)
|
||||||
|
|
||||||
self.app.worker_task.emit({'fcn': worker_task, 'params': []})
|
self.app.worker_task.emit({'fcn': worker_task, 'params': []})
|
||||||
|
|
||||||
@@ -294,7 +292,7 @@ class FlatCAMObj(QtCore.QObject):
|
|||||||
self.app.proc_container.update_view_text('')
|
self.app.proc_container.update_view_text('')
|
||||||
with self.app.proc_container.new('%s...' % _("Plotting")):
|
with self.app.proc_container.new('%s...' % _("Plotting")):
|
||||||
self.plot()
|
self.plot()
|
||||||
self.app.object_changed.emit(self)
|
self.app.app_obj.object_changed.emit(self)
|
||||||
|
|
||||||
self.app.worker_task.emit({'fcn': worker_task, 'params': []})
|
self.app.worker_task.emit({'fcn': worker_task, 'params': []})
|
||||||
|
|
||||||
@@ -308,8 +306,8 @@ class FlatCAMObj(QtCore.QObject):
|
|||||||
for option in self.options:
|
for option in self.options:
|
||||||
try:
|
try:
|
||||||
self.set_form_item(option)
|
self.set_form_item(option)
|
||||||
except Exception:
|
except Exception as err:
|
||||||
self.app.log.warning("Unexpected error:", sys.exc_info())
|
self.app.log.warning("Unexpected error: %s" % str(sys.exc_info()), str(err))
|
||||||
|
|
||||||
def read_form(self):
|
def read_form(self):
|
||||||
"""
|
"""
|
||||||
@@ -323,7 +321,7 @@ class FlatCAMObj(QtCore.QObject):
|
|||||||
try:
|
try:
|
||||||
self.read_form_item(option)
|
self.read_form_item(option)
|
||||||
except Exception:
|
except Exception:
|
||||||
self.app.log.warning("Unexpected error:", sys.exc_info())
|
self.app.log.warning("Unexpected error: %s" % str(sys.exc_info()))
|
||||||
|
|
||||||
def set_form_item(self, option):
|
def set_form_item(self, option):
|
||||||
"""
|
"""
|
||||||
@@ -374,7 +372,7 @@ class FlatCAMObj(QtCore.QObject):
|
|||||||
def plot_task():
|
def plot_task():
|
||||||
with self.app.proc_container.new('%s...' % _("Plotting")):
|
with self.app.proc_container.new('%s...' % _("Plotting")):
|
||||||
self.plot()
|
self.plot()
|
||||||
self.app.object_changed.emit(self)
|
self.app.app_obj.object_changed.emit(self)
|
||||||
|
|
||||||
self.app.worker_task.emit({'fcn': plot_task, 'params': []})
|
self.app.worker_task.emit({'fcn': plot_task, 'params': []})
|
||||||
|
|
||||||
@@ -461,8 +459,13 @@ class FlatCAMObj(QtCore.QObject):
|
|||||||
def visible(self, value, threaded=True):
|
def visible(self, value, threaded=True):
|
||||||
log.debug("FlatCAMObj.visible()")
|
log.debug("FlatCAMObj.visible()")
|
||||||
|
|
||||||
def worker_task(app_obj):
|
# self.shapes.visible = value # maybe this is slower in VisPy? use enabled property?
|
||||||
self.shapes.visible = value
|
if self.shapes.visible is True:
|
||||||
|
if value is False:
|
||||||
|
self.shapes.visible = False
|
||||||
|
else:
|
||||||
|
if value is True:
|
||||||
|
self.shapes.visible = True
|
||||||
|
|
||||||
if self.app.is_legacy is False:
|
if self.app.is_legacy is False:
|
||||||
# Not all object types has annotations
|
# Not all object types has annotations
|
||||||
@@ -471,11 +474,6 @@ class FlatCAMObj(QtCore.QObject):
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if threaded is False:
|
|
||||||
worker_task(app_obj=self.app)
|
|
||||||
else:
|
|
||||||
self.app.worker_task.emit({'fcn': worker_task, 'params': [self]})
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def drawing_tolerance(self):
|
def drawing_tolerance(self):
|
||||||
self.units = self.app.defaults['units'].upper()
|
self.units = self.app.defaults['units'].upper()
|
||||||
@@ -10,16 +10,16 @@
|
|||||||
# File modified by: Marius Stanciu #
|
# File modified by: Marius Stanciu #
|
||||||
# ##########################################################
|
# ##########################################################
|
||||||
|
|
||||||
from flatcamEditors.FlatCAMTextEditor import TextEditor
|
from AppEditors.FlatCAMTextEditor import TextEditor
|
||||||
from flatcamObjects.FlatCAMObj import *
|
from AppObjects.FlatCAMObj import *
|
||||||
from flatcamGUI.ObjectUI import *
|
from AppGUI.ObjectUI import *
|
||||||
|
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
import sys
|
import sys
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -183,7 +183,13 @@ class ScriptObject(FlatCAMObj):
|
|||||||
if self.app.ui.shell_dock.isHidden():
|
if self.app.ui.shell_dock.isHidden():
|
||||||
self.app.ui.shell_dock.show()
|
self.app.ui.shell_dock.show()
|
||||||
|
|
||||||
self.script_code = deepcopy(self.script_editor_tab.code_editor.toPlainText())
|
self.app.shell.open_processing() # Disables input box.
|
||||||
|
|
||||||
|
# make sure that the pixmaps are not updated when running this as they will crash
|
||||||
|
# TODO find why the pixmaps load crash when run from this object (perhaps another thread?)
|
||||||
|
self.app.ui.fcinfo.lock_pmaps = True
|
||||||
|
|
||||||
|
self.script_code = self.script_editor_tab.code_editor.toPlainText()
|
||||||
|
|
||||||
old_line = ''
|
old_line = ''
|
||||||
for tcl_command_line in self.script_code.splitlines():
|
for tcl_command_line in self.script_code.splitlines():
|
||||||
@@ -202,8 +208,6 @@ class ScriptObject(FlatCAMObj):
|
|||||||
|
|
||||||
# execute the actual Tcl command
|
# execute the actual Tcl command
|
||||||
try:
|
try:
|
||||||
self.app.shell.open_processing() # Disables input box.
|
|
||||||
|
|
||||||
result = self.app.shell.tcl.eval(str(new_command))
|
result = self.app.shell.tcl.eval(str(new_command))
|
||||||
if result != 'None':
|
if result != 'None':
|
||||||
self.app.shell.append_output(result + '\n')
|
self.app.shell.append_output(result + '\n')
|
||||||
@@ -220,6 +224,7 @@ class ScriptObject(FlatCAMObj):
|
|||||||
log.error("Exec command Exception: %s\n" % result)
|
log.error("Exec command Exception: %s\n" % result)
|
||||||
self.app.shell.append_error('ERROR: %s\n '% result)
|
self.app.shell.append_error('ERROR: %s\n '% result)
|
||||||
|
|
||||||
|
self.app.ui.fcinfo.lock_pmaps = False
|
||||||
self.app.shell.close_processing()
|
self.app.shell.close_processing()
|
||||||
|
|
||||||
def on_autocomplete_changed(self, state):
|
def on_autocomplete_changed(self, state):
|
||||||
@@ -228,6 +233,27 @@ class ScriptObject(FlatCAMObj):
|
|||||||
else:
|
else:
|
||||||
self.script_editor_tab.code_editor.completer_enable = False
|
self.script_editor_tab.code_editor.completer_enable = False
|
||||||
|
|
||||||
|
def mirror(self, axis, point):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def offset(self, vect):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def rotate(self, angle, point):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def scale(self, xfactor, yfactor=None, point=None):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def skew(self, angle_x, angle_y, point):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def buffer(self, distance, join, factor=None):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def bounds(self, flatten=False):
|
||||||
|
return None, None, None, None
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
"""
|
"""
|
||||||
Returns a representation of the object as a dictionary.
|
Returns a representation of the object as a dictionary.
|
||||||
@@ -16,13 +16,13 @@ from PyQt5.QtCore import Qt, QSettings
|
|||||||
from PyQt5.QtGui import QColor
|
from PyQt5.QtGui import QColor
|
||||||
# from PyQt5.QtCore import QModelIndex
|
# from PyQt5.QtCore import QModelIndex
|
||||||
|
|
||||||
from flatcamObjects.FlatCAMObj import FlatCAMObj
|
from AppObjects.FlatCAMObj import FlatCAMObj
|
||||||
from flatcamObjects.FlatCAMCNCJob import CNCJobObject
|
from AppObjects.FlatCAMCNCJob import CNCJobObject
|
||||||
from flatcamObjects.FlatCAMDocument import DocumentObject
|
from AppObjects.FlatCAMDocument import DocumentObject
|
||||||
from flatcamObjects.FlatCAMExcellon import ExcellonObject
|
from AppObjects.FlatCAMExcellon import ExcellonObject
|
||||||
from flatcamObjects.FlatCAMGeometry import GeometryObject
|
from AppObjects.FlatCAMGeometry import GeometryObject
|
||||||
from flatcamObjects.FlatCAMGerber import GerberObject
|
from AppObjects.FlatCAMGerber import GerberObject
|
||||||
from flatcamObjects.FlatCAMScript import ScriptObject
|
from AppObjects.FlatCAMScript import ScriptObject
|
||||||
|
|
||||||
import inspect # TODO: Remove
|
import inspect # TODO: Remove
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ from copy import deepcopy
|
|||||||
from numpy import Inf
|
from numpy import Inf
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -50,12 +50,13 @@ class KeySensitiveListView(QtWidgets.QTreeView):
|
|||||||
def __init__(self, app, parent=None):
|
def __init__(self, app, parent=None):
|
||||||
super(KeySensitiveListView, self).__init__(parent)
|
super(KeySensitiveListView, self).__init__(parent)
|
||||||
self.setHeaderHidden(True)
|
self.setHeaderHidden(True)
|
||||||
self.setEditTriggers(QtWidgets.QTreeView.SelectedClicked)
|
# self.setEditTriggers(QtWidgets.QTreeView.SelectedClicked) # allow Edit on Tree
|
||||||
|
self.setEditTriggers(QtWidgets.QTreeView.NoEditTriggers)
|
||||||
|
|
||||||
# self.setRootIsDecorated(False)
|
# self.setRootIsDecorated(False)
|
||||||
# self.setExpandsOnDoubleClick(False)
|
# self.setExpandsOnDoubleClick(False)
|
||||||
|
|
||||||
# Enable dragging and dropping onto the GUI
|
# Enable dragging and dropping onto the AppGUI
|
||||||
self.setAcceptDrops(True)
|
self.setAcceptDrops(True)
|
||||||
self.filename = ""
|
self.filename = ""
|
||||||
self.app = app
|
self.app = app
|
||||||
@@ -334,6 +335,8 @@ class ObjectCollection(QtCore.QAbstractItemModel):
|
|||||||
self.click_modifier = None
|
self.click_modifier = None
|
||||||
|
|
||||||
self.update_list_signal.connect(self.on_update_list_signal)
|
self.update_list_signal.connect(self.on_update_list_signal)
|
||||||
|
self.view.activated.connect(self.on_row_activated)
|
||||||
|
self.item_selected.connect(self.on_row_selected)
|
||||||
|
|
||||||
def promise(self, obj_name):
|
def promise(self, obj_name):
|
||||||
log.debug("Object %s has been promised." % obj_name)
|
log.debug("Object %s has been promised." % obj_name)
|
||||||
@@ -382,8 +385,9 @@ class ObjectCollection(QtCore.QAbstractItemModel):
|
|||||||
|
|
||||||
if type(obj) != GeometryObject:
|
if type(obj) != GeometryObject:
|
||||||
self.app.ui.menuprojectgeneratecnc.setVisible(False)
|
self.app.ui.menuprojectgeneratecnc.setVisible(False)
|
||||||
if type(obj) != GeometryObject and type(obj) != ExcellonObject and type(obj) != GerberObject:
|
# if type(obj) != GeometryObject and type(obj) != ExcellonObject and type(obj) != GerberObject or \
|
||||||
self.app.ui.menuprojectedit.setVisible(False)
|
# type(obj) != CNCJobObject:
|
||||||
|
# self.app.ui.menuprojectedit.setVisible(False)
|
||||||
if type(obj) != GerberObject and type(obj) != ExcellonObject and type(obj) != CNCJobObject:
|
if type(obj) != GerberObject and type(obj) != ExcellonObject and type(obj) != CNCJobObject:
|
||||||
self.app.ui.menuprojectviewsource.setVisible(False)
|
self.app.ui.menuprojectviewsource.setVisible(False)
|
||||||
if type(obj) != GerberObject and type(obj) != GeometryObject and type(obj) != ExcellonObject and \
|
if type(obj) != GerberObject and type(obj) != GeometryObject and type(obj) != ExcellonObject and \
|
||||||
@@ -550,7 +554,7 @@ class ObjectCollection(QtCore.QAbstractItemModel):
|
|||||||
while name in self.get_names():
|
while name in self.get_names():
|
||||||
# ## Create a new name
|
# ## Create a new name
|
||||||
# Ends with number?
|
# Ends with number?
|
||||||
log.debug("new_object(): Object name (%s) exists, changing." % name)
|
log.debug("app_obj.new_object(): Object name (%s) exists, changing." % name)
|
||||||
match = re.search(r'(.*[^\d])?(\d+)$', name)
|
match = re.search(r'(.*[^\d])?(\d+)$', name)
|
||||||
if match: # Yes: Increment the number!
|
if match: # Yes: Increment the number!
|
||||||
base = match.group(1) or ''
|
base = match.group(1) or ''
|
||||||
@@ -985,7 +989,7 @@ class ObjectCollection(QtCore.QAbstractItemModel):
|
|||||||
|
|
||||||
def get_list(self):
|
def get_list(self):
|
||||||
"""
|
"""
|
||||||
Will return a list of all objects currently opened.
|
Will return a list of all objects currently opened. Except FlatCAMScript and FlatCAMDocuments
|
||||||
|
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
@@ -998,3 +1002,199 @@ class ObjectCollection(QtCore.QAbstractItemModel):
|
|||||||
|
|
||||||
def update_view(self):
|
def update_view(self):
|
||||||
self.dataChanged.emit(QtCore.QModelIndex(), QtCore.QModelIndex())
|
self.dataChanged.emit(QtCore.QModelIndex(), QtCore.QModelIndex())
|
||||||
|
|
||||||
|
def on_row_activated(self, index):
|
||||||
|
if index.isValid():
|
||||||
|
if index.internalPointer().parent_item != self.root_item:
|
||||||
|
self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
|
||||||
|
self.on_item_activated(index)
|
||||||
|
|
||||||
|
def on_row_selected(self, obj_name):
|
||||||
|
"""
|
||||||
|
This is a special string; when received it will make all Menu -> Objects entries unchecked
|
||||||
|
It mean we clicked outside of the items and deselected all
|
||||||
|
|
||||||
|
:param obj_name:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if obj_name == 'none':
|
||||||
|
for act in self.app.ui.menuobjects.actions():
|
||||||
|
act.setChecked(False)
|
||||||
|
return
|
||||||
|
|
||||||
|
# get the name of the selected objects and add them to a list
|
||||||
|
name_list = []
|
||||||
|
for obj in self.get_selected():
|
||||||
|
name_list.append(obj.options['name'])
|
||||||
|
|
||||||
|
# set all actions as unchecked but the ones selected make them checked
|
||||||
|
for act in self.app.ui.menuobjects.actions():
|
||||||
|
act.setChecked(False)
|
||||||
|
if act.text() in name_list:
|
||||||
|
act.setChecked(True)
|
||||||
|
|
||||||
|
def on_collection_updated(self, obj, state, old_name):
|
||||||
|
"""
|
||||||
|
Create a menu from the object loaded in the collection.
|
||||||
|
|
||||||
|
:param obj: object that was changed (added, deleted, renamed)
|
||||||
|
:param state: what was done with the object. Can be: added, deleted, delete_all, renamed
|
||||||
|
:param old_name: the old name of the object before the action that triggered this slot happened
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
icon_files = {
|
||||||
|
"gerber": self.app.resource_location + "/flatcam_icon16.png",
|
||||||
|
"excellon": self.app.resource_location + "/drill16.png",
|
||||||
|
"cncjob": self.app.resource_location + "/cnc16.png",
|
||||||
|
"geometry": self.app.resource_location + "/geometry16.png",
|
||||||
|
"script": self.app.resource_location + "/script_new16.png",
|
||||||
|
"document": self.app.resource_location + "/notes16_1.png"
|
||||||
|
}
|
||||||
|
|
||||||
|
if state == 'append':
|
||||||
|
for act in self.app.ui.menuobjects.actions():
|
||||||
|
try:
|
||||||
|
act.triggered.disconnect()
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
self.app.ui.menuobjects.clear()
|
||||||
|
|
||||||
|
gerber_list = []
|
||||||
|
exc_list = []
|
||||||
|
cncjob_list = []
|
||||||
|
geo_list = []
|
||||||
|
script_list = []
|
||||||
|
doc_list = []
|
||||||
|
|
||||||
|
for name in self.get_names():
|
||||||
|
obj_named = self.get_by_name(name)
|
||||||
|
if obj_named.kind == 'gerber':
|
||||||
|
gerber_list.append(name)
|
||||||
|
elif obj_named.kind == 'excellon':
|
||||||
|
exc_list.append(name)
|
||||||
|
elif obj_named.kind == 'cncjob':
|
||||||
|
cncjob_list.append(name)
|
||||||
|
elif obj_named.kind == 'geometry':
|
||||||
|
geo_list.append(name)
|
||||||
|
elif obj_named.kind == 'script':
|
||||||
|
script_list.append(name)
|
||||||
|
elif obj_named.kind == 'document':
|
||||||
|
doc_list.append(name)
|
||||||
|
|
||||||
|
def add_act(o_name):
|
||||||
|
obj_for_icon = self.get_by_name(o_name)
|
||||||
|
add_action = QtWidgets.QAction(parent=self.app.ui.menuobjects)
|
||||||
|
add_action.setCheckable(True)
|
||||||
|
add_action.setText(o_name)
|
||||||
|
add_action.setIcon(QtGui.QIcon(icon_files[obj_for_icon.kind]))
|
||||||
|
add_action.triggered.connect(
|
||||||
|
lambda: self.set_active(o_name) if add_action.isChecked() is True else
|
||||||
|
self.set_inactive(o_name))
|
||||||
|
self.app.ui.menuobjects.addAction(add_action)
|
||||||
|
|
||||||
|
for name in gerber_list:
|
||||||
|
add_act(name)
|
||||||
|
self.app.ui.menuobjects.addSeparator()
|
||||||
|
|
||||||
|
for name in exc_list:
|
||||||
|
add_act(name)
|
||||||
|
self.app.ui.menuobjects.addSeparator()
|
||||||
|
|
||||||
|
for name in cncjob_list:
|
||||||
|
add_act(name)
|
||||||
|
self.app.ui.menuobjects.addSeparator()
|
||||||
|
|
||||||
|
for name in geo_list:
|
||||||
|
add_act(name)
|
||||||
|
self.app.ui.menuobjects.addSeparator()
|
||||||
|
|
||||||
|
for name in script_list:
|
||||||
|
add_act(name)
|
||||||
|
self.app.ui.menuobjects.addSeparator()
|
||||||
|
|
||||||
|
for name in doc_list:
|
||||||
|
add_act(name)
|
||||||
|
|
||||||
|
self.app.ui.menuobjects.addSeparator()
|
||||||
|
self.app.ui.menuobjects_selall = self.app.ui.menuobjects.addAction(
|
||||||
|
QtGui.QIcon(self.app.resource_location + '/select_all.png'),
|
||||||
|
_('Select All')
|
||||||
|
)
|
||||||
|
self.app.ui.menuobjects_unselall = self.app.ui.menuobjects.addAction(
|
||||||
|
QtGui.QIcon(self.app.resource_location + '/deselect_all32.png'),
|
||||||
|
_('Deselect All')
|
||||||
|
)
|
||||||
|
self.app.ui.menuobjects_selall.triggered.connect(lambda: self.on_objects_selection(True))
|
||||||
|
self.app.ui.menuobjects_unselall.triggered.connect(lambda: self.on_objects_selection(False))
|
||||||
|
|
||||||
|
elif state == 'delete':
|
||||||
|
for act in self.app.ui.menuobjects.actions():
|
||||||
|
if act.text() == obj.options['name']:
|
||||||
|
try:
|
||||||
|
act.triggered.disconnect()
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
self.app.ui.menuobjects.removeAction(act)
|
||||||
|
break
|
||||||
|
elif state == 'rename':
|
||||||
|
for act in self.app.ui.menuobjects.actions():
|
||||||
|
if act.text() == old_name:
|
||||||
|
add_action = QtWidgets.QAction(parent=self.app.ui.menuobjects)
|
||||||
|
add_action.setText(obj.options['name'])
|
||||||
|
add_action.setIcon(QtGui.QIcon(icon_files[obj.kind]))
|
||||||
|
add_action.triggered.connect(
|
||||||
|
lambda: self.set_active(obj.options['name']) if add_action.isChecked() is True else
|
||||||
|
self.set_inactive(obj.options['name']))
|
||||||
|
|
||||||
|
self.app.ui.menuobjects.insertAction(act, add_action)
|
||||||
|
|
||||||
|
try:
|
||||||
|
act.triggered.disconnect()
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
self.app.ui.menuobjects.removeAction(act)
|
||||||
|
break
|
||||||
|
elif state == 'delete_all':
|
||||||
|
for act in self.app.ui.menuobjects.actions():
|
||||||
|
try:
|
||||||
|
act.triggered.disconnect()
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
self.app.ui.menuobjects.clear()
|
||||||
|
|
||||||
|
self.app.ui.menuobjects.addSeparator()
|
||||||
|
self.app.ui.menuobjects_selall = self.app.ui.menuobjects.addAction(
|
||||||
|
QtGui.QIcon(self.app.resource_location + '/select_all.png'),
|
||||||
|
_('Select All')
|
||||||
|
)
|
||||||
|
self.app.ui.menuobjects_unselall = self.app.ui.menuobjects.addAction(
|
||||||
|
QtGui.QIcon(self.app.resource_location + '/deselect_all32.png'),
|
||||||
|
_('Deselect All')
|
||||||
|
)
|
||||||
|
self.app.ui.menuobjects_selall.triggered.connect(lambda: self.on_objects_selection(True))
|
||||||
|
self.app.ui.menuobjects_unselall.triggered.connect(lambda: self.on_objects_selection(False))
|
||||||
|
|
||||||
|
def on_objects_selection(self, on_off):
|
||||||
|
obj_list = self.get_names()
|
||||||
|
|
||||||
|
if on_off is True:
|
||||||
|
self.set_all_active()
|
||||||
|
for act in self.app.ui.menuobjects.actions():
|
||||||
|
try:
|
||||||
|
act.setChecked(True)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if obj_list:
|
||||||
|
self.app.inform[str, bool].emit('[selected] %s' % _("All objects are selected."), False)
|
||||||
|
else:
|
||||||
|
self.set_all_inactive()
|
||||||
|
for act in self.app.ui.menuobjects.actions():
|
||||||
|
try:
|
||||||
|
act.setChecked(False)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if obj_list:
|
||||||
|
self.app.inform[str, bool].emit('%s' % _("Objects selection is cleared."), False)
|
||||||
|
else:
|
||||||
|
self.app.inform[str, bool].emit('', False)
|
||||||
@@ -12,8 +12,8 @@ import logging
|
|||||||
|
|
||||||
log = logging.getLogger('base2')
|
log = logging.getLogger('base2')
|
||||||
|
|
||||||
from flatcamParsers.ParseFont import *
|
from AppParsers.ParseFont import *
|
||||||
from flatcamParsers.ParseDXF_Spline import *
|
from AppParsers.ParseDXF_Spline import *
|
||||||
|
|
||||||
|
|
||||||
def distance(pt1, pt2):
|
def distance(pt1, pt2):
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
# MIT Licence #
|
# MIT Licence #
|
||||||
# ########################################################## ##
|
# ########################################################## ##
|
||||||
|
|
||||||
from camlib import Geometry
|
from camlib import Geometry, grace
|
||||||
|
|
||||||
import shapely.affinity as affinity
|
import shapely.affinity as affinity
|
||||||
from shapely.geometry import Point, LineString
|
from shapely.geometry import Point, LineString
|
||||||
@@ -17,8 +17,7 @@ import logging
|
|||||||
import traceback
|
import traceback
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
import FlatCAMTranslation as fcTranslate
|
# import AppTranslation as fcTranslate
|
||||||
from FlatCAMCommon import GracefulException as grace
|
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import builtins
|
import builtins
|
||||||
@@ -967,7 +966,7 @@ class Excellon(Geometry):
|
|||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
log.debug("flatcamParsers.ParseExcellon.Excellon.create_geometry()")
|
log.debug("AppParsers.ParseExcellon.Excellon.create_geometry()")
|
||||||
self.solid_geometry = []
|
self.solid_geometry = []
|
||||||
try:
|
try:
|
||||||
# clear the solid_geometry in self.tools
|
# clear the solid_geometry in self.tools
|
||||||
@@ -982,7 +981,7 @@ class Excellon(Geometry):
|
|||||||
_("Excellon.create_geometry() -> a drill location was skipped "
|
_("Excellon.create_geometry() -> a drill location was skipped "
|
||||||
"due of not having a tool associated.\n"
|
"due of not having a tool associated.\n"
|
||||||
"Check the resulting GCode."))
|
"Check the resulting GCode."))
|
||||||
log.debug("flatcamParsers.ParseExcellon.Excellon.create_geometry() -> a drill location was skipped "
|
log.debug("AppParsers.ParseExcellon.Excellon.create_geometry() -> a drill location was skipped "
|
||||||
"due of not having a tool associated")
|
"due of not having a tool associated")
|
||||||
continue
|
continue
|
||||||
tooldia = self.tools[drill['tool']]['C']
|
tooldia = self.tools[drill['tool']]['C']
|
||||||
@@ -1006,7 +1005,7 @@ class Excellon(Geometry):
|
|||||||
self.tools[tool_in_slots]['solid_geometry'].append(poly)
|
self.tools[tool_in_slots]['solid_geometry'].append(poly)
|
||||||
self.tools[tool_in_slots]['data'] = deepcopy(self.default_data)
|
self.tools[tool_in_slots]['data'] = deepcopy(self.default_data)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.debug("flatcamParsers.ParseExcellon.Excellon.create_geometry() -> "
|
log.debug("AppParsers.ParseExcellon.Excellon.create_geometry() -> "
|
||||||
"Excellon geometry creation failed due of ERROR: %s" % str(e))
|
"Excellon geometry creation failed due of ERROR: %s" % str(e))
|
||||||
return "fail"
|
return "fail"
|
||||||
|
|
||||||
@@ -1018,10 +1017,10 @@ class Excellon(Geometry):
|
|||||||
:param flatten: No used
|
:param flatten: No used
|
||||||
"""
|
"""
|
||||||
|
|
||||||
log.debug("flatcamParsers.ParseExcellon.Excellon.bounds()")
|
log.debug("AppParsers.ParseExcellon.Excellon.bounds()")
|
||||||
|
|
||||||
if self.solid_geometry is None or not self.tools:
|
if self.solid_geometry is None or not self.tools:
|
||||||
log.debug("flatcamParsers.ParseExcellon.Excellon -> solid_geometry is None")
|
log.debug("AppParsers.ParseExcellon.Excellon -> solid_geometry is None")
|
||||||
return 0, 0, 0, 0
|
return 0, 0, 0, 0
|
||||||
|
|
||||||
def bounds_rec(obj):
|
def bounds_rec(obj):
|
||||||
@@ -1069,7 +1068,7 @@ class Excellon(Geometry):
|
|||||||
This function first convert to the the units found in the Excellon file but it converts tools that
|
This function first convert to the the units found in the Excellon file but it converts tools that
|
||||||
are not there yet so it has no effect other than it signal that the units are the ones in the file.
|
are not there yet so it has no effect other than it signal that the units are the ones in the file.
|
||||||
|
|
||||||
On object creation, in new_object(), true conversion is done because this is done at the end of the
|
On object creation, in app_obj.new_object(), true conversion is done because this is done at the end of the
|
||||||
Excellon file parsing, the tools are inside and self.tools is really converted from the units found
|
Excellon file parsing, the tools are inside and self.tools is really converted from the units found
|
||||||
inside the file to the FlatCAM units.
|
inside the file to the FlatCAM units.
|
||||||
|
|
||||||
@@ -1092,7 +1091,7 @@ class Excellon(Geometry):
|
|||||||
else:
|
else:
|
||||||
log.error("Unsupported units: %s" % str(obj_units))
|
log.error("Unsupported units: %s" % str(obj_units))
|
||||||
factor = 1.0
|
factor = 1.0
|
||||||
log.debug("flatcamParsers.ParseExcellon.Excellon.convert_units() --> Factor: %s" % str(factor))
|
log.debug("AppParsers.ParseExcellon.Excellon.convert_units() --> Factor: %s" % str(factor))
|
||||||
|
|
||||||
self.units = obj_units
|
self.units = obj_units
|
||||||
self.scale(factor, factor)
|
self.scale(factor, factor)
|
||||||
@@ -1118,7 +1117,7 @@ class Excellon(Geometry):
|
|||||||
:return: None
|
:return: None
|
||||||
:rtype: None
|
:rtype: None
|
||||||
"""
|
"""
|
||||||
log.debug("flatcamParsers.ParseExcellon.Excellon.scale()")
|
log.debug("AppParsers.ParseExcellon.Excellon.scale()")
|
||||||
|
|
||||||
if yfactor is None:
|
if yfactor is None:
|
||||||
yfactor = xfactor
|
yfactor = xfactor
|
||||||
@@ -1183,7 +1182,7 @@ class Excellon(Geometry):
|
|||||||
:type vect: tuple
|
:type vect: tuple
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
log.debug("flatcamParsers.ParseExcellon.Excellon.offset()")
|
log.debug("AppParsers.ParseExcellon.Excellon.offset()")
|
||||||
|
|
||||||
dx, dy = vect
|
dx, dy = vect
|
||||||
|
|
||||||
@@ -1243,7 +1242,7 @@ class Excellon(Geometry):
|
|||||||
:type point: list
|
:type point: list
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
log.debug("flatcamParsers.ParseExcellon.Excellon.mirror()")
|
log.debug("AppParsers.ParseExcellon.Excellon.mirror()")
|
||||||
|
|
||||||
px, py = point
|
px, py = point
|
||||||
xscale, yscale = {"X": (1.0, -1.0), "Y": (-1.0, 1.0)}[axis]
|
xscale, yscale = {"X": (1.0, -1.0), "Y": (-1.0, 1.0)}[axis]
|
||||||
@@ -1309,7 +1308,7 @@ class Excellon(Geometry):
|
|||||||
See shapely manual for more information:
|
See shapely manual for more information:
|
||||||
http://toblerity.org/shapely/manual.html#affine-transformations
|
http://toblerity.org/shapely/manual.html#affine-transformations
|
||||||
"""
|
"""
|
||||||
log.debug("flatcamParsers.ParseExcellon.Excellon.skew()")
|
log.debug("AppParsers.ParseExcellon.Excellon.skew()")
|
||||||
|
|
||||||
if angle_x is None:
|
if angle_x is None:
|
||||||
angle_x = 0.0
|
angle_x = 0.0
|
||||||
@@ -1396,7 +1395,7 @@ class Excellon(Geometry):
|
|||||||
:param point: tuple of coordinates (x, y)
|
:param point: tuple of coordinates (x, y)
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
log.debug("flatcamParsers.ParseExcellon.Excellon.rotate()")
|
log.debug("AppParsers.ParseExcellon.Excellon.rotate()")
|
||||||
|
|
||||||
if angle == 0:
|
if angle == 0:
|
||||||
return
|
return
|
||||||
@@ -1479,7 +1478,7 @@ class Excellon(Geometry):
|
|||||||
:param join: The type of line joint used by the shapely buffer method: round, square, bevel
|
:param join: The type of line joint used by the shapely buffer method: round, square, bevel
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
log.debug("flatcamParsers.ParseExcellon.Excellon.buffer()")
|
log.debug("AppParsers.ParseExcellon.Excellon.buffer()")
|
||||||
|
|
||||||
if distance == 0:
|
if distance == 0:
|
||||||
return
|
return
|
||||||
@@ -22,7 +22,7 @@ from fontTools import ttLib
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import gettext
|
import gettext
|
||||||
import FlatCAMTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
fcTranslate.apply_language('strings')
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from camlib import Geometry, arc, arc_angle, ApertureMacro
|
from camlib import Geometry, arc, arc_angle, ApertureMacro, grace
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import re
|
import re
|
||||||
@@ -14,9 +14,8 @@ import shapely.affinity as affinity
|
|||||||
from shapely.geometry import box as shply_box, Polygon, LineString, Point, MultiPolygon
|
from shapely.geometry import box as shply_box, Polygon, LineString, Point, MultiPolygon
|
||||||
|
|
||||||
from lxml import etree as ET
|
from lxml import etree as ET
|
||||||
from flatcamParsers.ParseSVG import svgparselength, getsvggeo
|
from AppParsers.ParseSVG import svgparselength, getsvggeo
|
||||||
from FlatCAMCommon import GracefulException as grace
|
import AppTranslation as fcTranslate
|
||||||
import FlatCAMTranslation as fcTranslate
|
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import builtins
|
import builtins
|
||||||
@@ -1477,7 +1476,7 @@ class Gerber(Geometry):
|
|||||||
sol_geo_length = 1
|
sol_geo_length = 1
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if buff_length == 0 and sol_geo_length in [0, 1]:
|
if buff_length == 0 and sol_geo_length in [0, 1] and self.solid_geometry.area == 0:
|
||||||
log.error("Object is not Gerber file or empty. Aborting Object creation.")
|
log.error("Object is not Gerber file or empty. Aborting Object creation.")
|
||||||
return 'fail'
|
return 'fail'
|
||||||
except TypeError as e:
|
except TypeError as e:
|
||||||
@@ -1763,7 +1762,7 @@ class Gerber(Geometry):
|
|||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
log.debug("flatcamParsers.ParseGerber.Gerber.import_svg()")
|
log.debug("AppParsers.ParseGerber.Gerber.import_svg()")
|
||||||
|
|
||||||
# Parse into list of shapely objects
|
# Parse into list of shapely objects
|
||||||
svg_tree = ET.parse(filename)
|
svg_tree = ET.parse(filename)
|
||||||
@@ -2389,7 +2388,7 @@ class Gerber(Geometry):
|
|||||||
geo_p = shply_box(minx, miny, maxx, maxy)
|
geo_p = shply_box(minx, miny, maxx, maxy)
|
||||||
new_geo_el['solid'] = geo_p
|
new_geo_el['solid'] = geo_p
|
||||||
else:
|
else:
|
||||||
log.debug("flatcamParsers.ParseGerber.Gerber.buffer() --> "
|
log.debug("AppParsers.ParseGerber.Gerber.buffer() --> "
|
||||||
"ap type not supported")
|
"ap type not supported")
|
||||||
else:
|
else:
|
||||||
new_geo_el['solid'] = geo_el['follow'].buffer(
|
new_geo_el['solid'] = geo_el['follow'].buffer(
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
# MIT Licence #
|
# MIT Licence #
|
||||||
# ############################################################
|
# ############################################################
|
||||||
|
|
||||||
from camlib import arc, three_point_circle
|
from camlib import arc, three_point_circle, grace
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import re
|
import re
|
||||||
@@ -18,8 +18,7 @@ import sys
|
|||||||
from shapely.ops import unary_union
|
from shapely.ops import unary_union
|
||||||
from shapely.geometry import LineString, Point
|
from shapely.geometry import LineString, Point
|
||||||
|
|
||||||
from FlatCAMCommon import GracefulException as grace
|
# import AppTranslation as fcTranslate
|
||||||
import FlatCAMTranslation as fcTranslate
|
|
||||||
import gettext
|
import gettext
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
@@ -5,50 +5,27 @@
|
|||||||
# MIT Licence #
|
# MIT Licence #
|
||||||
# ##########################################################
|
# ##########################################################
|
||||||
|
|
||||||
from PyQt5 import QtWidgets, QtCore
|
from PyQt5 import QtCore
|
||||||
|
|
||||||
from FlatCAMTool import FlatCAMTool
|
from Common import GracefulException as grace
|
||||||
from FlatCAMCommon import GracefulException as grace
|
|
||||||
|
|
||||||
from shapely.geometry import Point, Polygon, LineString, MultiPolygon
|
from shapely.geometry import Polygon, LineString, MultiPolygon
|
||||||
from shapely.ops import unary_union
|
|
||||||
|
|
||||||
from copy import copy, deepcopy
|
from copy import copy, deepcopy
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
import zlib
|
|
||||||
import re
|
import re
|
||||||
import time
|
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
|
||||||
|
|
||||||
import gettext
|
|
||||||
import FlatCAMTranslation as fcTranslate
|
|
||||||
import builtins
|
|
||||||
|
|
||||||
fcTranslate.apply_language('strings')
|
|
||||||
if '_' not in builtins.__dict__:
|
|
||||||
_ = gettext.gettext
|
|
||||||
|
|
||||||
log = logging.getLogger('base')
|
log = logging.getLogger('base')
|
||||||
|
|
||||||
|
|
||||||
class ToolPDF(FlatCAMTool):
|
class PdfParser(QtCore.QObject):
|
||||||
"""
|
|
||||||
Parse a PDF file.
|
|
||||||
Reference here: https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/pdf_reference_archives/PDFReference.pdf
|
|
||||||
Return a list of geometries
|
|
||||||
"""
|
|
||||||
toolName = _("PDF Import Tool")
|
|
||||||
|
|
||||||
def __init__(self, app):
|
def __init__(self, app):
|
||||||
FlatCAMTool.__init__(self, app)
|
super().__init__()
|
||||||
self.app = app
|
self.app = app
|
||||||
self.decimals = self.app.decimals
|
|
||||||
self.step_per_circles = self.app.defaults["gerber_circle_steps"]
|
self.step_per_circles = self.app.defaults["gerber_circle_steps"]
|
||||||
|
|
||||||
self.stream_re = re.compile(b'.*?FlateDecode.*?stream(.*?)endstream', re.S)
|
|
||||||
|
|
||||||
# detect stroke color change; it means a new object to be created
|
# detect stroke color change; it means a new object to be created
|
||||||
self.stroke_color_re = re.compile(r'^\s*(\d+\.?\d*) (\d+\.?\d*) (\d+\.?\d*)\s*RG$')
|
self.stroke_color_re = re.compile(r'^\s*(\d+\.?\d*) (\d+\.?\d*) (\d+\.?\d*)\s*RG$')
|
||||||
|
|
||||||
@@ -112,73 +89,10 @@ class ToolPDF(FlatCAMTool):
|
|||||||
self.gs['transform'] = []
|
self.gs['transform'] = []
|
||||||
self.gs['line_width'] = [] # each element is a float
|
self.gs['line_width'] = [] # each element is a float
|
||||||
|
|
||||||
self.pdf_decompressed = {}
|
|
||||||
|
|
||||||
# key = file name and extension
|
|
||||||
# value is a dict to store the parsed content of the PDF
|
|
||||||
self.pdf_parsed = {}
|
|
||||||
|
|
||||||
# QTimer for periodic check
|
|
||||||
self.check_thread = QtCore.QTimer()
|
|
||||||
|
|
||||||
# Every time a parser is started we add a promise; every time a parser finished we remove a promise
|
|
||||||
# when empty we start the layer rendering
|
|
||||||
self.parsing_promises = []
|
|
||||||
|
|
||||||
# conversion factor to INCH
|
# conversion factor to INCH
|
||||||
self.point_to_unit_factor = 0.01388888888
|
self.point_to_unit_factor = 0.01388888888
|
||||||
|
|
||||||
def run(self, toggle=True):
|
def parse_pdf(self, pdf_content):
|
||||||
self.app.defaults.report_usage("ToolPDF()")
|
|
||||||
|
|
||||||
self.set_tool_ui()
|
|
||||||
self.on_open_pdf_click()
|
|
||||||
|
|
||||||
def install(self, icon=None, separator=None, **kwargs):
|
|
||||||
FlatCAMTool.install(self, icon, separator, shortcut='Ctrl+Q', **kwargs)
|
|
||||||
|
|
||||||
def set_tool_ui(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_open_pdf_click(self):
|
|
||||||
"""
|
|
||||||
File menu callback for opening an PDF file.
|
|
||||||
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.app.defaults.report_usage("ToolPDF.on_open_pdf_click()")
|
|
||||||
self.app.log.debug("ToolPDF.on_open_pdf_click()")
|
|
||||||
|
|
||||||
_filter_ = "Adobe PDF Files (*.pdf);;" \
|
|
||||||
"All Files (*.*)"
|
|
||||||
|
|
||||||
try:
|
|
||||||
filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open PDF"),
|
|
||||||
directory=self.app.get_last_folder(),
|
|
||||||
filter=_filter_)
|
|
||||||
except TypeError:
|
|
||||||
filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open PDF"), filter=_filter_)
|
|
||||||
|
|
||||||
if len(filenames) == 0:
|
|
||||||
self.app.inform.emit('[WARNING_NOTCL] %s.' % _("Open PDF cancelled"))
|
|
||||||
else:
|
|
||||||
# start the parsing timer with a period of 1 second
|
|
||||||
self.periodic_check(1000)
|
|
||||||
|
|
||||||
for filename in filenames:
|
|
||||||
if filename != '':
|
|
||||||
self.app.worker_task.emit({'fcn': self.open_pdf,
|
|
||||||
'params': [filename]})
|
|
||||||
|
|
||||||
def open_pdf(self, filename):
|
|
||||||
short_name = filename.split('/')[-1].split('\\')[-1]
|
|
||||||
self.parsing_promises.append(short_name)
|
|
||||||
self.pdf_parsed[short_name] = {}
|
|
||||||
self.pdf_parsed[short_name]['pdf'] = {}
|
|
||||||
self.pdf_parsed[short_name]['filename'] = filename
|
|
||||||
|
|
||||||
self.pdf_decompressed[short_name] = ''
|
|
||||||
|
|
||||||
# the UNITS in PDF files are points and here we set the factor to convert them to real units (either MM or INCH)
|
# the UNITS in PDF files are points and here we set the factor to convert them to real units (either MM or INCH)
|
||||||
if self.app.defaults['units'].upper() == 'MM':
|
if self.app.defaults['units'].upper() == 'MM':
|
||||||
@@ -188,252 +102,6 @@ class ToolPDF(FlatCAMTool):
|
|||||||
# 1 inch = 72 points => 1 point = 1 / 72 = 0.01388888888 inch
|
# 1 inch = 72 points => 1 point = 1 / 72 = 0.01388888888 inch
|
||||||
self.point_to_unit_factor = 1 / 72
|
self.point_to_unit_factor = 1 / 72
|
||||||
|
|
||||||
if self.app.abort_flag:
|
|
||||||
# graceful abort requested by the user
|
|
||||||
raise grace
|
|
||||||
|
|
||||||
with self.app.proc_container.new(_("Parsing PDF file ...")):
|
|
||||||
with open(filename, "rb") as f:
|
|
||||||
pdf = f.read()
|
|
||||||
|
|
||||||
stream_nr = 0
|
|
||||||
for s in re.findall(self.stream_re, pdf):
|
|
||||||
if self.app.abort_flag:
|
|
||||||
# graceful abort requested by the user
|
|
||||||
raise grace
|
|
||||||
|
|
||||||
stream_nr += 1
|
|
||||||
log.debug(" PDF STREAM: %d\n" % stream_nr)
|
|
||||||
s = s.strip(b'\r\n')
|
|
||||||
try:
|
|
||||||
self.pdf_decompressed[short_name] += (zlib.decompress(s).decode('UTF-8') + '\r\n')
|
|
||||||
except Exception as e:
|
|
||||||
log.debug("ToolPDF.open_pdf().obj_init() --> %s" % str(e))
|
|
||||||
|
|
||||||
self.pdf_parsed[short_name]['pdf'] = self.parse_pdf(pdf_content=self.pdf_decompressed[short_name])
|
|
||||||
# we used it, now we delete it
|
|
||||||
self.pdf_decompressed[short_name] = ''
|
|
||||||
|
|
||||||
# removal from list is done in a multithreaded way therefore not always the removal can be done
|
|
||||||
# try to remove until it's done
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
self.parsing_promises.remove(short_name)
|
|
||||||
time.sleep(0.1)
|
|
||||||
except Exception as e:
|
|
||||||
log.debug("ToolPDF.open_pdf() --> %s" % str(e))
|
|
||||||
self.app.inform.emit('[success] %s: %s' % (_("Opened"), str(filename)))
|
|
||||||
|
|
||||||
def layer_rendering_as_excellon(self, filename, ap_dict, layer_nr):
|
|
||||||
outname = filename.split('/')[-1].split('\\')[-1] + "_%s" % str(layer_nr)
|
|
||||||
|
|
||||||
# store the points here until reconstitution:
|
|
||||||
# keys are diameters and values are list of (x,y) coords
|
|
||||||
points = {}
|
|
||||||
|
|
||||||
def obj_init(exc_obj, app_obj):
|
|
||||||
clear_geo = [geo_el['clear'] for geo_el in ap_dict['0']['geometry']]
|
|
||||||
|
|
||||||
for geo in clear_geo:
|
|
||||||
xmin, ymin, xmax, ymax = geo.bounds
|
|
||||||
center = (((xmax - xmin) / 2) + xmin, ((ymax - ymin) / 2) + ymin)
|
|
||||||
|
|
||||||
# for drill bits, even in INCH, it's enough 3 decimals
|
|
||||||
correction_factor = 0.974
|
|
||||||
dia = (xmax - xmin) * correction_factor
|
|
||||||
dia = round(dia, 3)
|
|
||||||
if dia in points:
|
|
||||||
points[dia].append(center)
|
|
||||||
else:
|
|
||||||
points[dia] = [center]
|
|
||||||
|
|
||||||
sorted_dia = sorted(points.keys())
|
|
||||||
|
|
||||||
name_tool = 0
|
|
||||||
for dia in sorted_dia:
|
|
||||||
name_tool += 1
|
|
||||||
|
|
||||||
# create tools dictionary
|
|
||||||
spec = {"C": dia, 'solid_geometry': []}
|
|
||||||
exc_obj.tools[str(name_tool)] = spec
|
|
||||||
|
|
||||||
# create drill list of dictionaries
|
|
||||||
for dia_points in points:
|
|
||||||
if dia == dia_points:
|
|
||||||
for pt in points[dia_points]:
|
|
||||||
exc_obj.drills.append({'point': Point(pt), 'tool': str(name_tool)})
|
|
||||||
break
|
|
||||||
|
|
||||||
ret = exc_obj.create_geometry()
|
|
||||||
if ret == 'fail':
|
|
||||||
log.debug("Could not create geometry for Excellon object.")
|
|
||||||
return "fail"
|
|
||||||
for tool in exc_obj.tools:
|
|
||||||
if exc_obj.tools[tool]['solid_geometry']:
|
|
||||||
return
|
|
||||||
app_obj.inform.emit('[ERROR_NOTCL] %s: %s' %
|
|
||||||
(_("No geometry found in file"), outname))
|
|
||||||
return "fail"
|
|
||||||
|
|
||||||
with self.app.proc_container.new(_("Rendering PDF layer #%d ...") % int(layer_nr)):
|
|
||||||
|
|
||||||
ret_val = self.app.new_object("excellon", outname, obj_init, autoselected=False)
|
|
||||||
if ret_val == 'fail':
|
|
||||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
|
||||||
_('Open PDF file failed.'))
|
|
||||||
return
|
|
||||||
# Register recent file
|
|
||||||
self.app.file_opened.emit("excellon", filename)
|
|
||||||
# GUI feedback
|
|
||||||
self.app.inform.emit('[success] %s: %s' %
|
|
||||||
(_("Rendered"), outname))
|
|
||||||
|
|
||||||
def layer_rendering_as_gerber(self, filename, ap_dict, layer_nr):
|
|
||||||
outname = filename.split('/')[-1].split('\\')[-1] + "_%s" % str(layer_nr)
|
|
||||||
|
|
||||||
def obj_init(grb_obj):
|
|
||||||
|
|
||||||
grb_obj.apertures = ap_dict
|
|
||||||
|
|
||||||
poly_buff = []
|
|
||||||
follow_buf = []
|
|
||||||
for ap in grb_obj.apertures:
|
|
||||||
for k in grb_obj.apertures[ap]:
|
|
||||||
if k == 'geometry':
|
|
||||||
for geo_el in ap_dict[ap][k]:
|
|
||||||
if 'solid' in geo_el:
|
|
||||||
poly_buff.append(geo_el['solid'])
|
|
||||||
if 'follow' in geo_el:
|
|
||||||
follow_buf.append(geo_el['follow'])
|
|
||||||
poly_buff = unary_union(poly_buff)
|
|
||||||
|
|
||||||
if '0' in grb_obj.apertures:
|
|
||||||
global_clear_geo = []
|
|
||||||
if 'geometry' in grb_obj.apertures['0']:
|
|
||||||
for geo_el in ap_dict['0']['geometry']:
|
|
||||||
if 'clear' in geo_el:
|
|
||||||
global_clear_geo.append(geo_el['clear'])
|
|
||||||
|
|
||||||
if global_clear_geo:
|
|
||||||
solid = []
|
|
||||||
for apid in grb_obj.apertures:
|
|
||||||
if 'geometry' in grb_obj.apertures[apid]:
|
|
||||||
for elem in grb_obj.apertures[apid]['geometry']:
|
|
||||||
if 'solid' in elem:
|
|
||||||
solid_geo = deepcopy(elem['solid'])
|
|
||||||
for clear_geo in global_clear_geo:
|
|
||||||
# Make sure that the clear_geo is within the solid_geo otherwise we loose
|
|
||||||
# the solid_geometry. We want for clear_geometry just to cut into solid_geometry
|
|
||||||
# not to delete it
|
|
||||||
if clear_geo.within(solid_geo):
|
|
||||||
solid_geo = solid_geo.difference(clear_geo)
|
|
||||||
if solid_geo.is_empty:
|
|
||||||
solid_geo = elem['solid']
|
|
||||||
try:
|
|
||||||
for poly in solid_geo:
|
|
||||||
solid.append(poly)
|
|
||||||
except TypeError:
|
|
||||||
solid.append(solid_geo)
|
|
||||||
poly_buff = deepcopy(MultiPolygon(solid))
|
|
||||||
|
|
||||||
follow_buf = unary_union(follow_buf)
|
|
||||||
|
|
||||||
try:
|
|
||||||
poly_buff = poly_buff.buffer(0.0000001)
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
poly_buff = poly_buff.buffer(-0.0000001)
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
grb_obj.solid_geometry = deepcopy(poly_buff)
|
|
||||||
grb_obj.follow_geometry = deepcopy(follow_buf)
|
|
||||||
|
|
||||||
with self.app.proc_container.new(_("Rendering PDF layer #%d ...") % int(layer_nr)):
|
|
||||||
|
|
||||||
ret = self.app.new_object('gerber', outname, obj_init, autoselected=False)
|
|
||||||
if ret == 'fail':
|
|
||||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
|
||||||
_('Open PDF file failed.'))
|
|
||||||
return
|
|
||||||
# Register recent file
|
|
||||||
self.app.file_opened.emit('gerber', filename)
|
|
||||||
# GUI feedback
|
|
||||||
self.app.inform.emit('[success] %s: %s' % (_("Rendered"), outname))
|
|
||||||
|
|
||||||
def periodic_check(self, check_period):
|
|
||||||
"""
|
|
||||||
This function starts an QTimer and it will periodically check if parsing was done
|
|
||||||
|
|
||||||
:param check_period: time at which to check periodically if all plots finished to be plotted
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
|
|
||||||
# self.plot_thread = threading.Thread(target=lambda: self.check_plot_finished(check_period))
|
|
||||||
# self.plot_thread.start()
|
|
||||||
log.debug("ToolPDF --> Periodic Check started.")
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.check_thread.stop()
|
|
||||||
except TypeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
self.check_thread.setInterval(check_period)
|
|
||||||
try:
|
|
||||||
self.check_thread.timeout.disconnect(self.periodic_check_handler)
|
|
||||||
except (TypeError, AttributeError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
self.check_thread.timeout.connect(self.periodic_check_handler)
|
|
||||||
self.check_thread.start(QtCore.QThread.HighPriority)
|
|
||||||
|
|
||||||
def periodic_check_handler(self):
|
|
||||||
"""
|
|
||||||
If the parsing worker finished then start multithreaded rendering
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
# log.debug("checking parsing --> %s" % str(self.parsing_promises))
|
|
||||||
|
|
||||||
try:
|
|
||||||
if not self.parsing_promises:
|
|
||||||
self.check_thread.stop()
|
|
||||||
# parsing finished start the layer rendering
|
|
||||||
if self.pdf_parsed:
|
|
||||||
obj_to_delete = []
|
|
||||||
for object_name in self.pdf_parsed:
|
|
||||||
if self.app.abort_flag:
|
|
||||||
# graceful abort requested by the user
|
|
||||||
raise grace
|
|
||||||
|
|
||||||
filename = deepcopy(self.pdf_parsed[object_name]['filename'])
|
|
||||||
pdf_content = deepcopy(self.pdf_parsed[object_name]['pdf'])
|
|
||||||
obj_to_delete.append(object_name)
|
|
||||||
for k in pdf_content:
|
|
||||||
if self.app.abort_flag:
|
|
||||||
# graceful abort requested by the user
|
|
||||||
raise grace
|
|
||||||
|
|
||||||
ap_dict = pdf_content[k]
|
|
||||||
if ap_dict:
|
|
||||||
layer_nr = k
|
|
||||||
if k == 0:
|
|
||||||
self.app.worker_task.emit({'fcn': self.layer_rendering_as_excellon,
|
|
||||||
'params': [filename, ap_dict, layer_nr]})
|
|
||||||
else:
|
|
||||||
self.app.worker_task.emit({'fcn': self.layer_rendering_as_gerber,
|
|
||||||
'params': [filename, ap_dict, layer_nr]})
|
|
||||||
# delete the object already processed so it will not be processed again for other objects
|
|
||||||
# that were opened at the same time; like in drag & drop on GUI
|
|
||||||
for obj_name in obj_to_delete:
|
|
||||||
if obj_name in self.pdf_parsed:
|
|
||||||
self.pdf_parsed.pop(obj_name)
|
|
||||||
|
|
||||||
log.debug("ToolPDF --> Periodic check finished.")
|
|
||||||
except Exception:
|
|
||||||
traceback.print_exc()
|
|
||||||
|
|
||||||
def parse_pdf(self, pdf_content):
|
|
||||||
path = {}
|
path = {}
|
||||||
path['lines'] = [] # it's a list of lines subpaths
|
path['lines'] = [] # it's a list of lines subpaths
|
||||||
path['bezier'] = [] # it's a list of bezier arcs subpaths
|
path['bezier'] = [] # it's a list of bezier arcs subpaths
|
||||||
@@ -503,7 +171,7 @@ class ToolPDF(FlatCAMTool):
|
|||||||
if match:
|
if match:
|
||||||
color = [float(match.group(1)), float(match.group(2)), float(match.group(3))]
|
color = [float(match.group(1)), float(match.group(2)), float(match.group(3))]
|
||||||
log.debug(
|
log.debug(
|
||||||
"ToolPDF.parse_pdf() --> STROKE Color change on line: %s --> RED=%f GREEN=%f BLUE=%f" %
|
"parse_pdf() --> STROKE Color change on line: %s --> RED=%f GREEN=%f BLUE=%f" %
|
||||||
(line_nr, color[0], color[1], color[2]))
|
(line_nr, color[0], color[1], color[2]))
|
||||||
|
|
||||||
if color[0] == old_color[0] and color[1] == old_color[1] and color[2] == old_color[2]:
|
if color[0] == old_color[0] and color[1] == old_color[1] and color[2] == old_color[2]:
|
||||||
@@ -526,7 +194,7 @@ class ToolPDF(FlatCAMTool):
|
|||||||
if match:
|
if match:
|
||||||
fill_color = [float(match.group(1)), float(match.group(2)), float(match.group(3))]
|
fill_color = [float(match.group(1)), float(match.group(2)), float(match.group(3))]
|
||||||
log.debug(
|
log.debug(
|
||||||
"ToolPDF.parse_pdf() --> FILL Color change on line: %s --> RED=%f GREEN=%f BLUE=%f" %
|
"parse_pdf() --> FILL Color change on line: %s --> RED=%f GREEN=%f BLUE=%f" %
|
||||||
(line_nr, fill_color[0], fill_color[1], fill_color[2]))
|
(line_nr, fill_color[0], fill_color[1], fill_color[2]))
|
||||||
# if the color is white we are seeing 'clear_geometry' that can't be seen. It may be that those
|
# if the color is white we are seeing 'clear_geometry' that can't be seen. It may be that those
|
||||||
# geometries are actually holes from which we can make an Excellon file
|
# geometries are actually holes from which we can make an Excellon file
|
||||||
@@ -545,7 +213,7 @@ class ToolPDF(FlatCAMTool):
|
|||||||
# sometimes they combine save_to_graphics_stack with the transformation on the same line
|
# sometimes they combine save_to_graphics_stack with the transformation on the same line
|
||||||
if match.group(1) == 'q':
|
if match.group(1) == 'q':
|
||||||
log.debug(
|
log.debug(
|
||||||
"ToolPDF.parse_pdf() --> Save to GS found on line: %s --> offset=[%f, %f] ||| scale=[%f, %f]" %
|
"parse_pdf() --> Save to GS found on line: %s --> offset=[%f, %f] ||| scale=[%f, %f]" %
|
||||||
(line_nr, offset_geo[0], offset_geo[1], scale_geo[0], scale_geo[1]))
|
(line_nr, offset_geo[0], offset_geo[1], scale_geo[0], scale_geo[1]))
|
||||||
|
|
||||||
self.gs['transform'].append(deepcopy([offset_geo, scale_geo]))
|
self.gs['transform'].append(deepcopy([offset_geo, scale_geo]))
|
||||||
@@ -555,7 +223,7 @@ class ToolPDF(FlatCAMTool):
|
|||||||
if (float(match.group(3)) == 0 and float(match.group(4)) == 0) and \
|
if (float(match.group(3)) == 0 and float(match.group(4)) == 0) and \
|
||||||
(float(match.group(6)) != 0 or float(match.group(7)) != 0):
|
(float(match.group(6)) != 0 or float(match.group(7)) != 0):
|
||||||
log.debug(
|
log.debug(
|
||||||
"ToolPDF.parse_pdf() --> OFFSET transformation found on line: %s --> %s" % (line_nr, pline))
|
"parse_pdf() --> OFFSET transformation found on line: %s --> %s" % (line_nr, pline))
|
||||||
|
|
||||||
offset_geo[0] += float(match.group(6))
|
offset_geo[0] += float(match.group(6))
|
||||||
offset_geo[1] += float(match.group(7))
|
offset_geo[1] += float(match.group(7))
|
||||||
@@ -564,7 +232,7 @@ class ToolPDF(FlatCAMTool):
|
|||||||
# transformation = SCALING
|
# transformation = SCALING
|
||||||
if float(match.group(2)) != 1 and float(match.group(5)) != 1:
|
if float(match.group(2)) != 1 and float(match.group(5)) != 1:
|
||||||
log.debug(
|
log.debug(
|
||||||
"ToolPDF.parse_pdf() --> SCALE transformation found on line: %s --> %s" % (line_nr, pline))
|
"parse_pdf() --> SCALE transformation found on line: %s --> %s" % (line_nr, pline))
|
||||||
|
|
||||||
scale_geo[0] *= float(match.group(2))
|
scale_geo[0] *= float(match.group(2))
|
||||||
scale_geo[1] *= float(match.group(5))
|
scale_geo[1] *= float(match.group(5))
|
||||||
@@ -576,7 +244,7 @@ class ToolPDF(FlatCAMTool):
|
|||||||
match = self.save_gs_re.search(pline)
|
match = self.save_gs_re.search(pline)
|
||||||
if match:
|
if match:
|
||||||
log.debug(
|
log.debug(
|
||||||
"ToolPDF.parse_pdf() --> Save to GS found on line: %s --> offset=[%f, %f] ||| scale=[%f, %f]" %
|
"parse_pdf() --> Save to GS found on line: %s --> offset=[%f, %f] ||| scale=[%f, %f]" %
|
||||||
(line_nr, offset_geo[0], offset_geo[1], scale_geo[0], scale_geo[1]))
|
(line_nr, offset_geo[0], offset_geo[1], scale_geo[0], scale_geo[1]))
|
||||||
self.gs['transform'].append(deepcopy([offset_geo, scale_geo]))
|
self.gs['transform'].append(deepcopy([offset_geo, scale_geo]))
|
||||||
self.gs['line_width'].append(deepcopy(size))
|
self.gs['line_width'].append(deepcopy(size))
|
||||||
@@ -590,18 +258,18 @@ class ToolPDF(FlatCAMTool):
|
|||||||
scale_geo = restored_transform[1]
|
scale_geo = restored_transform[1]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
# nothing to remove
|
# nothing to remove
|
||||||
log.debug("ToolPDF.parse_pdf() --> Nothing to restore")
|
log.debug("parse_pdf() --> Nothing to restore")
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
size = self.gs['line_width'].pop(-1)
|
size = self.gs['line_width'].pop(-1)
|
||||||
except IndexError:
|
except IndexError:
|
||||||
log.debug("ToolPDF.parse_pdf() --> Nothing to restore")
|
log.debug("parse_pdf() --> Nothing to restore")
|
||||||
# nothing to remove
|
# nothing to remove
|
||||||
pass
|
pass
|
||||||
|
|
||||||
log.debug(
|
log.debug(
|
||||||
"ToolPDF.parse_pdf() --> Restore from GS found on line: %s --> "
|
"parse_pdf() --> Restore from GS found on line: %s --> "
|
||||||
"restored_offset=[%f, %f] ||| restored_scale=[%f, %f]" %
|
"restored_offset=[%f, %f] ||| restored_scale=[%f, %f]" %
|
||||||
(line_nr, offset_geo[0], offset_geo[1], scale_geo[0], scale_geo[1]))
|
(line_nr, offset_geo[0], offset_geo[1], scale_geo[0], scale_geo[1]))
|
||||||
# log.debug("Restored Offset= [%f, %f]" % (offset_geo[0], offset_geo[1]))
|
# log.debug("Restored Offset= [%f, %f]" % (offset_geo[0], offset_geo[1]))
|
||||||
@@ -712,9 +380,9 @@ class ToolPDF(FlatCAMTool):
|
|||||||
width = (float(match.group(3)) + offset_geo[0]) * self.point_to_unit_factor * scale_geo[0]
|
width = (float(match.group(3)) + offset_geo[0]) * self.point_to_unit_factor * scale_geo[0]
|
||||||
height = (float(match.group(4)) + offset_geo[1]) * self.point_to_unit_factor * scale_geo[1]
|
height = (float(match.group(4)) + offset_geo[1]) * self.point_to_unit_factor * scale_geo[1]
|
||||||
pt1 = (x, y)
|
pt1 = (x, y)
|
||||||
pt2 = (x+width, y)
|
pt2 = (x + width, y)
|
||||||
pt3 = (x+width, y+height)
|
pt3 = (x + width, y + height)
|
||||||
pt4 = (x, y+height)
|
pt4 = (x, y + height)
|
||||||
subpath['rectangle'] += [pt1, pt2, pt3, pt4, pt1]
|
subpath['rectangle'] += [pt1, pt2, pt3, pt4, pt1]
|
||||||
current_point = pt1
|
current_point = pt1
|
||||||
continue
|
continue
|
||||||
@@ -948,7 +616,8 @@ class ToolPDF(FlatCAMTool):
|
|||||||
geo += self.bezier_to_points(start=b[0], c1=b[1], c2=b[2], stop=b[3])
|
geo += self.bezier_to_points(start=b[0], c1=b[1], c2=b[2], stop=b[3])
|
||||||
# close the subpath if it was not closed already
|
# close the subpath if it was not closed already
|
||||||
if close_subpath is False:
|
if close_subpath is False:
|
||||||
geo.append(geo[0])
|
new_g = geo[0]
|
||||||
|
geo.append(new_g)
|
||||||
try:
|
try:
|
||||||
geo_el = Polygon(geo).buffer(0.0000001, resolution=self.step_per_circles)
|
geo_el = Polygon(geo).buffer(0.0000001, resolution=self.step_per_circles)
|
||||||
path_geo.append(geo_el)
|
path_geo.append(geo_el)
|
||||||
@@ -27,7 +27,7 @@ from shapely.geometry import LineString, LinearRing, MultiLineString
|
|||||||
from shapely.affinity import skew, affine_transform, rotate
|
from shapely.affinity import skew, affine_transform, rotate
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from flatcamParsers.ParseFont import *
|
from AppParsers.ParseFont import *
|
||||||
|
|
||||||
log = logging.getLogger('base2')
|
log = logging.getLogger('base2')
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user