- added to GUI new options for the Gerber object related to area subtraction
- added new feature in the Gerber object isolation allowing for the isolation to avoid an area defined by another object (Gerber or Geometry)
This commit is contained in:
parent
2c2bdf5002
commit
f164dae7a9
108
FlatCAMApp.py
108
FlatCAMApp.py
@ -1090,9 +1090,9 @@ class App(QtCore.QObject):
|
||||
if user_defaults:
|
||||
self.load_defaults(filename='current_defaults')
|
||||
|
||||
# ###########################
|
||||
# #### APPLY APP LANGUAGE ###
|
||||
# ###########################
|
||||
# #############################################################
|
||||
# ############## APPLY APP LANGUAGE ###########################
|
||||
# #############################################################
|
||||
|
||||
ret_val = fcTranslate.apply_language('strings')
|
||||
|
||||
@ -1104,9 +1104,9 @@ class App(QtCore.QObject):
|
||||
self.ui.general_defaults_form.general_app_group.language_cb.setCurrentText(ret_val)
|
||||
log.debug("App.__init__() --> Applied %s language." % str(ret_val).capitalize())
|
||||
|
||||
# ##################################
|
||||
# ### CREATE UNIQUE SERIAL NUMBER ##
|
||||
# ##################################
|
||||
# ##############################################################
|
||||
# ################# CREATE UNIQUE SERIAL NUMBER ################
|
||||
# ##############################################################
|
||||
|
||||
chars = 'abcdefghijklmnopqrstuvwxyz0123456789'
|
||||
if self.defaults['global_serial'] == 0 or len(str(self.defaults['global_serial'])) < 10:
|
||||
@ -1495,20 +1495,27 @@ class App(QtCore.QObject):
|
||||
# ############### Signal handling ################
|
||||
# ################################################
|
||||
|
||||
# ### Custom signals ###
|
||||
# ############# Custom signals ##################
|
||||
|
||||
# signal for displaying messages in status bar
|
||||
self.inform.connect(self.info)
|
||||
# signal to be called when the app is quiting
|
||||
self.app_quit.connect(self.quit_application)
|
||||
self.message.connect(self.message_dialog)
|
||||
self.progress.connect(self.set_progress_bar)
|
||||
|
||||
# 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.on_plots_updated)
|
||||
|
||||
# signals emitted when file state change
|
||||
self.file_opened.connect(self.register_recent)
|
||||
self.file_opened.connect(lambda kind, filename: self.register_folder(filename))
|
||||
self.file_saved.connect(lambda kind, filename: self.register_save_folder(filename))
|
||||
|
||||
# ### Standard signals
|
||||
# ############# Standard signals ###################
|
||||
# ### Menu
|
||||
self.ui.menufilenewproject.triggered.connect(self.on_file_new_click)
|
||||
self.ui.menufilenewgeo.triggered.connect(self.new_geometry_object)
|
||||
@ -1675,9 +1682,9 @@ class App(QtCore.QObject):
|
||||
self.ui.general_defaults_form.general_app_group.units_radio.activated_custom.connect(
|
||||
lambda: self.on_toggle_units(no_pref=False))
|
||||
|
||||
# ##############################
|
||||
# ### GUI PREFERENCES SIGNALS ##
|
||||
# ##############################
|
||||
# ###############################################################
|
||||
# ################### GUI COLORS SIGNALS ########################
|
||||
# ###############################################################
|
||||
|
||||
# Setting plot colors signals
|
||||
self.ui.general_defaults_form.general_gui_group.pf_color_entry.editingFinished.connect(
|
||||
@ -1739,11 +1746,13 @@ class App(QtCore.QObject):
|
||||
self.ui.general_defaults_form.general_gui_group.proj_color_dis_button.clicked.connect(
|
||||
self.on_proj_color_dis_button)
|
||||
|
||||
# ########## workspace setting signals ###########
|
||||
self.ui.general_defaults_form.general_gui_group.wk_cb.currentIndexChanged.connect(self.on_workspace_modified)
|
||||
self.ui.general_defaults_form.general_gui_group.workspace_cb.stateChanged.connect(self.on_workspace)
|
||||
|
||||
self.ui.general_defaults_form.general_gui_set_group.layout_combo.activated.connect(self.on_layout)
|
||||
|
||||
# ########## CNC Job related signals #############
|
||||
self.ui.cncjob_defaults_form.cncjob_adv_opt_group.tc_variable_combo.currentIndexChanged[str].connect(
|
||||
self.on_cnc_custom_parameters)
|
||||
self.ui.cncjob_defaults_form.cncjob_gen_group.annotation_fontcolor_entry.editingFinished.connect(
|
||||
@ -1751,7 +1760,7 @@ class App(QtCore.QObject):
|
||||
self.ui.cncjob_defaults_form.cncjob_gen_group.annotation_fontcolor_button.clicked.connect(
|
||||
self.on_annotation_fontcolor_button)
|
||||
|
||||
# Modify G-CODE Plot Area TAB
|
||||
# ########## Modify G-CODE Plot Area TAB ###########
|
||||
self.ui.code_editor.textChanged.connect(self.handleTextChanged)
|
||||
self.ui.buttonOpen.clicked.connect(self.handleOpen)
|
||||
self.ui.buttonSave.clicked.connect(self.handleSaveGCode)
|
||||
@ -1760,7 +1769,7 @@ class App(QtCore.QObject):
|
||||
self.ui.buttonFind.clicked.connect(self.handleFindGCode)
|
||||
self.ui.buttonReplace.clicked.connect(self.handleReplaceGCode)
|
||||
|
||||
# portability changed
|
||||
# portability changed signal
|
||||
self.ui.general_defaults_form.general_app_group.portability_cb.stateChanged.connect(self.on_portable_checked)
|
||||
|
||||
# Object list
|
||||
@ -1791,8 +1800,15 @@ class App(QtCore.QObject):
|
||||
# connect the abort_all_tasks related slots to the related signals
|
||||
self.proc_container.idle_flag.connect(self.app_is_idle)
|
||||
|
||||
# #####################################################################################
|
||||
# ########### FINISHED CONNECTING SIGNALS #############################################
|
||||
# #####################################################################################
|
||||
self.log.debug("Finished connecting Signals.")
|
||||
|
||||
# #####################################################################################
|
||||
# ########################## Other setups #############################################
|
||||
# #####################################################################################
|
||||
|
||||
# this is a flag to signal to other tools that the ui tooltab is locked and not accessible
|
||||
self.tool_tab_locked = False
|
||||
|
||||
@ -1802,18 +1818,14 @@ class App(QtCore.QObject):
|
||||
else:
|
||||
self.ui.splitter.setSizes([0, 1])
|
||||
|
||||
# ###########################################
|
||||
# ################# Other setups ############
|
||||
# ###########################################
|
||||
|
||||
# Sets up FlatCAMObj, FCProcess and FCProcessContainer.
|
||||
self.setup_obj_classes()
|
||||
self.setup_recent_items()
|
||||
self.setup_component_editor()
|
||||
|
||||
# ###########################################
|
||||
# #######Auto-complete KEYWORDS #############
|
||||
# ###########################################
|
||||
# #####################################################################################
|
||||
# ######################### Auto-complete KEYWORDS ####################################
|
||||
# #####################################################################################
|
||||
self.tcl_commands_list = ['add_circle', 'add_poly', 'add_polygon', 'add_polyline', 'add_rectangle',
|
||||
'aligndrill', 'aligndrillgrid', 'bbox', 'bounding_box', 'clear', 'cncjob', 'cutout',
|
||||
'delete', 'drillcncjob', 'export_gcode', 'export_svg', 'ext', 'exteriors', 'follow',
|
||||
@ -2028,9 +2040,9 @@ class App(QtCore.QObject):
|
||||
|
||||
self.myKeywords = self.tcl_commands_list + self.ordinary_keywords + self.tcl_keywords
|
||||
|
||||
# ###########################################
|
||||
# ########### Shell SETUP ###################
|
||||
# ###########################################
|
||||
# ####################################################################################
|
||||
# ####################### Shell SETUP ################################################
|
||||
# ####################################################################################
|
||||
|
||||
self.shell = FCShell(self, version=self.version)
|
||||
self.shell._edit.set_model_data(self.myKeywords)
|
||||
@ -2058,9 +2070,9 @@ class App(QtCore.QObject):
|
||||
else:
|
||||
self.ui.shell_dock.hide()
|
||||
|
||||
# ###########################################
|
||||
# ######### Tools and Plugins ###############
|
||||
# ###########################################
|
||||
# ##################################################################################
|
||||
# ###################### Tools and Plugins #########################################
|
||||
# ##################################################################################
|
||||
|
||||
self.dblsidedtool = None
|
||||
self.measurement_tool = None
|
||||
@ -2086,9 +2098,9 @@ class App(QtCore.QObject):
|
||||
# self.f_parse = ParseFont(self)
|
||||
# self.parse_system_fonts()
|
||||
|
||||
# ###############################################
|
||||
# ######## START-UP ARGUMENTS ###################
|
||||
# ###############################################
|
||||
# #####################################################################################
|
||||
# ########################## START-UP ARGUMENTS #######################################
|
||||
# #####################################################################################
|
||||
|
||||
# test if the program was started with a script as parameter
|
||||
if self.cmd_line_shellfile:
|
||||
@ -2100,9 +2112,9 @@ class App(QtCore.QObject):
|
||||
print("ERROR: ", ext)
|
||||
sys.exit(2)
|
||||
|
||||
# ###############################################
|
||||
# ############# Check for updates ###############
|
||||
# ###############################################
|
||||
# #####################################################################################
|
||||
# ######################## Check for updates ##########################################
|
||||
# #####################################################################################
|
||||
|
||||
# Separate thread (Not worker)
|
||||
# Check for updates on startup but only if the user consent and the app is not in Beta version
|
||||
@ -2115,9 +2127,9 @@ class App(QtCore.QObject):
|
||||
'params': []})
|
||||
self.thr2.start(QtCore.QThread.LowPriority)
|
||||
|
||||
# ################################################
|
||||
# ######### Variables for global usage ###########
|
||||
# ################################################
|
||||
# #####################################################################################
|
||||
# ###################### Variables for global usage ###################################
|
||||
# #####################################################################################
|
||||
|
||||
# register files with FlatCAM; it works only for Windows for now
|
||||
if sys.platform == 'win32' and self.defaults["first_run"] is True:
|
||||
@ -2199,10 +2211,10 @@ class App(QtCore.QObject):
|
||||
# when True, the app has to return from any thread
|
||||
self.abort_flag = False
|
||||
|
||||
# #########################################################
|
||||
# ### Save defaults to factory_defaults.FlatConfig file ###
|
||||
# ### It's done only once after install ###################
|
||||
# #########################################################
|
||||
# ###############################################################################
|
||||
# ############# Save defaults to factory_defaults.FlatConfig file ###############
|
||||
# ############# It's done only once after install ###############
|
||||
# ###############################################################################
|
||||
factory_file = open(self.data_path + '/factory_defaults.FlatConfig')
|
||||
fac_def_from_file = factory_file.read()
|
||||
factory_defaults = json.loads(fac_def_from_file)
|
||||
@ -2223,9 +2235,9 @@ class App(QtCore.QObject):
|
||||
filename_factory = self.data_path + '/factory_defaults.FlatConfig'
|
||||
os.chmod(filename_factory, S_IREAD | S_IRGRP | S_IROTH)
|
||||
|
||||
####################################################
|
||||
# ### ADDING FlatCAM EDITORS section ###############
|
||||
####################################################
|
||||
# ###############################################################################
|
||||
# ################# ADDING FlatCAM EDITORS section ##############################
|
||||
# ###############################################################################
|
||||
|
||||
# watch out for the position of the editors instantiation ... if it is done before a save of the default values
|
||||
# at the first launch of the App , the editors will not be functional.
|
||||
@ -2234,18 +2246,16 @@ class App(QtCore.QObject):
|
||||
self.grb_editor = FlatCAMGrbEditor(self)
|
||||
self.log.debug("Finished adding FlatCAM Editor's.")
|
||||
|
||||
# Post-GUI initialization: Experimental attempt
|
||||
# to perform unit tests on the GUI.
|
||||
# if post_gui is not None:
|
||||
# post_gui(self)
|
||||
|
||||
App.log.debug("END of constructor. Releasing control.")
|
||||
|
||||
self.set_ui_title(name=_("New Project - Not saved"))
|
||||
|
||||
# after the first run, this object should be False
|
||||
self.defaults["first_run"] = False
|
||||
|
||||
# ###############################################################################
|
||||
# ####################### Finished the CONSTRUCTOR ##############################
|
||||
# ###############################################################################
|
||||
App.log.debug("END of constructor. Releasing control.")
|
||||
|
||||
# accept some type file as command line parameter: FlatCAM project, FlatCAM preferences or scripts
|
||||
# the path/file_name must be enclosed in quotes if it contain spaces
|
||||
if App.args:
|
||||
|
131
FlatCAMObj.py
131
FlatCAMObj.py
@ -339,6 +339,10 @@ class FlatCAMObj(QtCore.QObject):
|
||||
key = self.mark_shapes[apid].add(tolerance=self.drawing_tolerance, **kwargs)
|
||||
return key
|
||||
|
||||
@staticmethod
|
||||
def poly2rings(poly):
|
||||
return [poly.exterior] + [interior for interior in poly.interiors]
|
||||
|
||||
@property
|
||||
def visible(self):
|
||||
return self.shapes.visible
|
||||
@ -551,6 +555,11 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
||||
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)
|
||||
|
||||
# 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.setCurrentIndex(1)
|
||||
self.ui.type_obj_combo.currentIndexChanged.connect(self.on_type_obj_index_changed)
|
||||
# Show/Hide Advanced Options
|
||||
if self.app.defaults["global_app_level"] == 'b':
|
||||
self.ui.level.setText(_(
|
||||
@ -563,12 +572,12 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
||||
self.ui.generate_ext_iso_button.hide()
|
||||
self.ui.generate_int_iso_button.hide()
|
||||
self.ui.follow_cb.hide()
|
||||
self.ui.padding_area_label.show()
|
||||
self.ui.except_cb.setChecked(False)
|
||||
self.ui.except_cb.hide()
|
||||
else:
|
||||
self.ui.level.setText(_(
|
||||
'<span style="color:red;"><b>Advanced</b></span>'
|
||||
))
|
||||
self.ui.padding_area_label.hide()
|
||||
|
||||
# add the shapes storage for marking apertures
|
||||
for ap_code in self.apertures:
|
||||
@ -579,6 +588,11 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
||||
|
||||
self.build_ui()
|
||||
|
||||
def on_type_obj_index_changed(self, index):
|
||||
obj_type = self.ui.type_obj_combo.currentIndex()
|
||||
self.ui.obj_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
|
||||
self.ui.obj_combo.setCurrentIndex(0)
|
||||
|
||||
def build_ui(self):
|
||||
FlatCAMObj.build_ui(self)
|
||||
|
||||
@ -881,23 +895,22 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
||||
|
||||
if invert:
|
||||
try:
|
||||
try:
|
||||
pl = []
|
||||
for p in geom:
|
||||
if p is not None:
|
||||
if isinstance(p, Polygon):
|
||||
pl.append(Polygon(p.exterior.coords[::-1], p.interiors))
|
||||
elif isinstance(p, LinearRing):
|
||||
pl.append(Polygon(p.coords[::-1]))
|
||||
geom = MultiPolygon(pl)
|
||||
except TypeError:
|
||||
if isinstance(geom, Polygon) and geom is not None:
|
||||
geom = Polygon(geom.exterior.coords[::-1], geom.interiors)
|
||||
elif isinstance(geom, LinearRing) and geom is not None:
|
||||
geom = Polygon(geom.coords[::-1])
|
||||
else:
|
||||
log.debug("FlatCAMGerber.isolate().generate_envelope() Error --> Unexpected Geometry %s" %
|
||||
type(geom))
|
||||
pl = []
|
||||
for p in geom:
|
||||
if p is not None:
|
||||
if isinstance(p, Polygon):
|
||||
pl.append(Polygon(p.exterior.coords[::-1], p.interiors))
|
||||
elif isinstance(p, LinearRing):
|
||||
pl.append(Polygon(p.coords[::-1]))
|
||||
geom = MultiPolygon(pl)
|
||||
except TypeError:
|
||||
if isinstance(geom, Polygon) and geom is not None:
|
||||
geom = Polygon(geom.exterior.coords[::-1], geom.interiors)
|
||||
elif isinstance(geom, LinearRing) and geom is not None:
|
||||
geom = Polygon(geom.coords[::-1])
|
||||
else:
|
||||
log.debug("FlatCAMGerber.isolate().generate_envelope() Error --> Unexpected Geometry %s" %
|
||||
type(geom))
|
||||
except Exception as e:
|
||||
log.debug("FlatCAMGerber.isolate().generate_envelope() Error --> %s" % str(e))
|
||||
return 'fail'
|
||||
@ -924,6 +937,54 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
||||
# if float(self.options["isotooldia"]) < 0:
|
||||
# self.options["isotooldia"] = -self.options["isotooldia"]
|
||||
|
||||
def area_subtraction(geo):
|
||||
new_geometry = []
|
||||
|
||||
name = self.ui.obj_combo.currentText()
|
||||
subtractor_obj = self.app.collection.get_by_name(name)
|
||||
sub_union = cascaded_union(subtractor_obj.solid_geometry)
|
||||
|
||||
try:
|
||||
for geo_elem in geo:
|
||||
if isinstance(geo_elem, Polygon):
|
||||
for ring in self.poly2rings(geo_elem):
|
||||
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(geo, Polygon):
|
||||
for ring in self.poly2rings(geo):
|
||||
new_geo = ring.difference(sub_union)
|
||||
if new_geo:
|
||||
if not new_geo.is_empty:
|
||||
new_geometry.append(new_geo)
|
||||
elif isinstance(geo, LineString):
|
||||
new_geo = geo.difference(sub_union)
|
||||
if new_geo and not new_geo.is_empty:
|
||||
new_geometry.append(new_geo)
|
||||
elif isinstance(geo, MultiLineString):
|
||||
for line_elem in 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
|
||||
|
||||
if combine:
|
||||
if self.iso_type == 0:
|
||||
iso_name = self.options["name"] + "_ext_iso"
|
||||
@ -1001,21 +1062,24 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
||||
|
||||
for g in geo_obj.solid_geometry:
|
||||
if g:
|
||||
app_obj.inform.emit(_(
|
||||
"[success] Isolation geometry created: %s"
|
||||
) % geo_obj.options["name"])
|
||||
break
|
||||
else:
|
||||
empty_cnt += 1
|
||||
|
||||
if empty_cnt == len(geo_obj.solid_geometry):
|
||||
raise ValidationError("Empty Geometry", None)
|
||||
|
||||
# even if combine is checked, one pass is still singlegeo
|
||||
if passes > 1:
|
||||
geo_obj.multigeo = True
|
||||
else:
|
||||
geo_obj.multigeo = False
|
||||
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():
|
||||
geo_obj.solid_geometry = 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)
|
||||
@ -1065,16 +1129,23 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
||||
|
||||
for g in geo_obj.solid_geometry:
|
||||
if g:
|
||||
app_obj.inform.emit(_(
|
||||
"[success] Isolation geometry created: %s"
|
||||
) % geo_obj.options["name"])
|
||||
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():
|
||||
geo_obj.solid_geometry = 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)
|
||||
|
||||
|
@ -14,6 +14,8 @@ CAD program, and create G-Code for Isolation routing.
|
||||
- added a method to gracefully exit from threaded tasks and implemented it for the NCC Tool and for the Paint Tool
|
||||
- modified the on_about() function to reflect the reality in 2019 - FlatCAM it is an Open Source contributed software
|
||||
- remade the handlers for the Enable/Disable Project Tree context menu so they are threaded and activity is shown in the lower right corner of the main window
|
||||
- added to GUI new options for the Gerber object related to area subtraction
|
||||
- added new feature in the Gerber object isolation allowing for the isolation to avoid an area defined by another object (Gerber or Geometry)
|
||||
|
||||
6.09.2019
|
||||
|
||||
|
@ -1524,6 +1524,45 @@ class OptionalInputSection:
|
||||
widget.setEnabled(True)
|
||||
|
||||
|
||||
class OptionalHideInputSection:
|
||||
|
||||
def __init__(self, cb, optinputs, logic=True):
|
||||
"""
|
||||
Associates the a checkbox with a set of inputs.
|
||||
|
||||
:param cb: Checkbox that enables the optional inputs.
|
||||
:param optinputs: List of widgets that are optional.
|
||||
: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
|
||||
for logic=False, when the checkbox is checked the widgets are Disabled
|
||||
:return:
|
||||
"""
|
||||
assert isinstance(cb, FCCheckBox), \
|
||||
"Expected an FCCheckBox, got %s" % type(cb)
|
||||
|
||||
self.cb = cb
|
||||
self.optinputs = optinputs
|
||||
self.logic = logic
|
||||
|
||||
self.on_cb_change()
|
||||
self.cb.stateChanged.connect(self.on_cb_change)
|
||||
|
||||
def on_cb_change(self):
|
||||
|
||||
if self.cb.checkState():
|
||||
for widget in self.optinputs:
|
||||
if self.logic is True:
|
||||
widget.show()
|
||||
else:
|
||||
widget.hide()
|
||||
else:
|
||||
for widget in self.optinputs:
|
||||
if self.logic is True:
|
||||
widget.hide()
|
||||
else:
|
||||
widget.show()
|
||||
|
||||
|
||||
class FCTable(QtWidgets.QTableWidget):
|
||||
def __init__(self, parent=None):
|
||||
super(FCTable, self).__init__(parent)
|
||||
|
@ -254,8 +254,13 @@ class GerberObjectUI(ObjectUI):
|
||||
)
|
||||
self.custom_box.addWidget(self.isolation_routing_label)
|
||||
|
||||
# ###########################################
|
||||
# ########## NEW GRID #######################
|
||||
# ###########################################
|
||||
|
||||
grid1 = QtWidgets.QGridLayout()
|
||||
self.custom_box.addLayout(grid1)
|
||||
|
||||
tdlabel = QtWidgets.QLabel('%s:' % _('Tool dia'))
|
||||
tdlabel.setToolTip(
|
||||
_("Diameter of the cutting tool.\n"
|
||||
@ -265,9 +270,9 @@ class GerberObjectUI(ObjectUI):
|
||||
"this parameter.")
|
||||
)
|
||||
tdlabel.setMinimumWidth(90)
|
||||
grid1.addWidget(tdlabel, 0, 0)
|
||||
self.iso_tool_dia_entry = LengthEntry()
|
||||
grid1.addWidget(self.iso_tool_dia_entry, 0, 1)
|
||||
grid1.addWidget(tdlabel, 0, 0)
|
||||
grid1.addWidget(self.iso_tool_dia_entry, 0, 1, 1, 2)
|
||||
|
||||
passlabel = QtWidgets.QLabel('%s:' % _('# Passes'))
|
||||
passlabel.setToolTip(
|
||||
@ -275,10 +280,10 @@ class GerberObjectUI(ObjectUI):
|
||||
"number (integer) of tool widths.")
|
||||
)
|
||||
passlabel.setMinimumWidth(90)
|
||||
grid1.addWidget(passlabel, 1, 0)
|
||||
self.iso_width_entry = FCSpinner()
|
||||
self.iso_width_entry.setRange(1, 999)
|
||||
grid1.addWidget(self.iso_width_entry, 1, 1)
|
||||
grid1.addWidget(passlabel, 1, 0)
|
||||
grid1.addWidget(self.iso_width_entry, 1, 1, 1, 2)
|
||||
|
||||
overlabel = QtWidgets.QLabel('%s:' % _('Pass overlap'))
|
||||
overlabel.setToolTip(
|
||||
@ -287,9 +292,9 @@ class GerberObjectUI(ObjectUI):
|
||||
"A value here of 0.25 means an overlap of 25% from the tool diameter found above.")
|
||||
)
|
||||
overlabel.setMinimumWidth(90)
|
||||
grid1.addWidget(overlabel, 2, 0)
|
||||
self.iso_overlap_entry = FloatEntry()
|
||||
grid1.addWidget(self.iso_overlap_entry, 2, 1)
|
||||
grid1.addWidget(overlabel, 2, 0)
|
||||
grid1.addWidget(self.iso_overlap_entry, 2, 1, 1, 2)
|
||||
|
||||
# Milling Type Radio Button
|
||||
self.milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type'))
|
||||
@ -298,27 +303,69 @@ class GerberObjectUI(ObjectUI):
|
||||
"- climb / best for precision milling and to reduce tool usage\n"
|
||||
"- conventional / useful when there is no backlash compensation")
|
||||
)
|
||||
grid1.addWidget(self.milling_type_label, 3, 0)
|
||||
self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
|
||||
{'label': _('Conv.'), 'value': 'cv'}])
|
||||
grid1.addWidget(self.milling_type_radio, 3, 1)
|
||||
grid1.addWidget(self.milling_type_label, 3, 0)
|
||||
grid1.addWidget(self.milling_type_radio, 3, 1, 1, 2)
|
||||
|
||||
# combine all passes CB
|
||||
self.combine_passes_cb = FCCheckBox(label=_('Combine Passes'))
|
||||
self.combine_passes_cb.setToolTip(
|
||||
_("Combine all passes into one object")
|
||||
)
|
||||
grid1.addWidget(self.combine_passes_cb, 4, 0)
|
||||
|
||||
# generate follow
|
||||
self.follow_cb = FCCheckBox(label=_('"Follow"'))
|
||||
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.setToolTip(_("Generate a 'Follow' geometry.\n"
|
||||
"This means that it will cut through\n"
|
||||
"the middle of the trace."))
|
||||
|
||||
)
|
||||
# avoid an area from isolation
|
||||
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 bellow\n"
|
||||
"will be subtracted from the isolation geometry."))
|
||||
|
||||
grid1.addWidget(self.combine_passes_cb, 4, 0)
|
||||
grid1.addWidget(self.follow_cb, 4, 1)
|
||||
grid1.addWidget(self.except_cb, 4, 2)
|
||||
|
||||
# ## Form Layout
|
||||
form_layout = QtWidgets.QFormLayout()
|
||||
grid1.addLayout(form_layout, 5, 0, 1, 3)
|
||||
|
||||
# ################################################
|
||||
# ##### Type of object to be excepted ############
|
||||
# ################################################
|
||||
self.type_obj_combo = QtWidgets.QComboBox()
|
||||
self.type_obj_combo.addItem("Gerber")
|
||||
self.type_obj_combo.addItem("Excellon")
|
||||
self.type_obj_combo.addItem("Geometry")
|
||||
|
||||
# we get rid of item1 ("Excellon") as it is not suitable
|
||||
self.type_obj_combo.view().setRowHidden(1, True)
|
||||
self.type_obj_combo.setItemIcon(0, QtGui.QIcon("share/flatcam_icon16.png"))
|
||||
self.type_obj_combo.setItemIcon(2, QtGui.QIcon("share/geometry16.png"))
|
||||
|
||||
self.type_obj_combo_label = QtWidgets.QLabel('%s:' % _("Obj Type"))
|
||||
self.type_obj_combo_label.setToolTip(
|
||||
_("Specify the type of object to be excepted from isolation.\n"
|
||||
"It can be of type: Gerber or Geometry.\n"
|
||||
"What is selected here will dictate the kind\n"
|
||||
"of objects that will populate the 'Object' combobox.")
|
||||
)
|
||||
# self.type_obj_combo_label.setMinimumWidth(60)
|
||||
form_layout.addRow(self.type_obj_combo_label, self.type_obj_combo)
|
||||
|
||||
# ################################################
|
||||
# ##### The object to be excepted ################
|
||||
# ################################################
|
||||
self.obj_combo = QtWidgets.QComboBox()
|
||||
|
||||
self.obj_label = QtWidgets.QLabel('%s:' % _("Object"))
|
||||
self.obj_label.setToolTip(_("Object whose area will be removed from isolation geometry."))
|
||||
|
||||
form_layout.addRow(self.obj_label, self.obj_combo)
|
||||
|
||||
self.gen_iso_label = QtWidgets.QLabel("<b>%s:</b>" % _("Generate Isolation Geometry"))
|
||||
self.gen_iso_label.setToolTip(
|
||||
@ -332,14 +379,7 @@ class GerberObjectUI(ObjectUI):
|
||||
"inside the actual Gerber feature, use a negative tool\n"
|
||||
"diameter above.")
|
||||
)
|
||||
self.custom_box.addWidget(self.gen_iso_label)
|
||||
|
||||
hlay_1 = QtWidgets.QHBoxLayout()
|
||||
self.custom_box.addLayout(hlay_1)
|
||||
|
||||
self.padding_area_label = QtWidgets.QLabel('')
|
||||
self.padding_area_label.setMinimumWidth(90)
|
||||
hlay_1.addWidget(self.padding_area_label)
|
||||
grid1.addWidget(self.gen_iso_label, 6, 0, 1, 3)
|
||||
|
||||
self.generate_iso_button = QtWidgets.QPushButton(_('FULL Geo'))
|
||||
self.generate_iso_button.setToolTip(
|
||||
@ -347,10 +387,10 @@ class GerberObjectUI(ObjectUI):
|
||||
"for isolation routing. It contains both\n"
|
||||
"the interiors and exteriors geometry.")
|
||||
)
|
||||
self.generate_iso_button.setMinimumWidth(90)
|
||||
hlay_1.addWidget(self.generate_iso_button, alignment=Qt.AlignLeft)
|
||||
grid1.addWidget(self.generate_iso_button, 7, 0)
|
||||
|
||||
# hlay_1.addStretch()
|
||||
hlay_1 = QtWidgets.QHBoxLayout()
|
||||
grid1.addLayout(hlay_1, 7, 1, 1, 2)
|
||||
|
||||
self.generate_ext_iso_button = QtWidgets.QPushButton(_('Ext Geo'))
|
||||
self.generate_ext_iso_button.setToolTip(
|
||||
@ -370,14 +410,28 @@ class GerberObjectUI(ObjectUI):
|
||||
# self.generate_ext_iso_button.setMinimumWidth(90)
|
||||
hlay_1.addWidget(self.generate_int_iso_button)
|
||||
|
||||
self.ohis_iso = OptionalHideInputSection(
|
||||
self.except_cb,
|
||||
[self.type_obj_combo, self.type_obj_combo_label, self.obj_combo, self.obj_label],
|
||||
logic=True
|
||||
)
|
||||
# when the follow checkbox is checked then the exteriors and interiors isolation generation buttons
|
||||
# are disabled as is doesn't make sense to have them enabled due of the nature of "follow"
|
||||
self.ois_iso = OptionalInputSection(self.follow_cb,
|
||||
[self.generate_int_iso_button, self.generate_ext_iso_button], logic=False)
|
||||
|
||||
grid1.addWidget(QtWidgets.QLabel(''), 8, 0)
|
||||
|
||||
# ###########################################
|
||||
# ########## NEW GRID #######################
|
||||
# ###########################################
|
||||
|
||||
grid2 = QtWidgets.QGridLayout()
|
||||
self.custom_box.addLayout(grid2)
|
||||
|
||||
self.tool_lbl = QtWidgets.QLabel('<b>%s</b>:' % _("TOOLS"))
|
||||
grid2.addWidget(self.tool_lbl, 0, 0, 1, 2)
|
||||
|
||||
# ## Clear non-copper regions
|
||||
self.clearcopper_label = QtWidgets.QLabel("<b>%s:</b>" % _("Clear N-copper"))
|
||||
self.clearcopper_label.setToolTip(
|
||||
@ -385,14 +439,14 @@ class GerberObjectUI(ObjectUI):
|
||||
"toolpaths to cut all non-copper regions.")
|
||||
)
|
||||
self.clearcopper_label.setMinimumWidth(90)
|
||||
grid2.addWidget(self.clearcopper_label, 0, 0)
|
||||
|
||||
self.generate_ncc_button = QtWidgets.QPushButton(_('NCC Tool'))
|
||||
self.generate_ncc_button.setToolTip(
|
||||
_("Create the Geometry Object\n"
|
||||
"for non-copper routing.")
|
||||
)
|
||||
grid2.addWidget(self.generate_ncc_button, 0, 1)
|
||||
grid2.addWidget(self.clearcopper_label, 1, 0)
|
||||
grid2.addWidget(self.generate_ncc_button, 1, 1)
|
||||
|
||||
# ## Board cutout
|
||||
self.board_cutout_label = QtWidgets.QLabel("<b>%s:</b>" % _("Board cutout"))
|
||||
@ -401,14 +455,14 @@ class GerberObjectUI(ObjectUI):
|
||||
"the PCB and separate it from\n"
|
||||
"the original board.")
|
||||
)
|
||||
grid2.addWidget(self.board_cutout_label, 1, 0)
|
||||
|
||||
self.generate_cutout_button = QtWidgets.QPushButton(_('Cutout Tool'))
|
||||
self.generate_cutout_button.setToolTip(
|
||||
_("Generate the geometry for\n"
|
||||
"the board cutout.")
|
||||
)
|
||||
grid2.addWidget(self.generate_cutout_button, 1, 1)
|
||||
grid2.addWidget(self.board_cutout_label, 2, 0)
|
||||
grid2.addWidget(self.generate_cutout_button, 2, 1)
|
||||
|
||||
# ## Non-copper regions
|
||||
self.noncopper_label = QtWidgets.QLabel("<b>%s:</b>" % _("Non-copper regions"))
|
||||
@ -419,10 +473,8 @@ class GerberObjectUI(ObjectUI):
|
||||
"object. Can be used to remove all\n"
|
||||
"copper from a specified region.")
|
||||
)
|
||||
self.custom_box.addWidget(self.noncopper_label)
|
||||
|
||||
grid4 = QtWidgets.QGridLayout()
|
||||
self.custom_box.addLayout(grid4)
|
||||
grid2.addWidget(self.noncopper_label, 3, 0, 1, 2)
|
||||
|
||||
# Margin
|
||||
bmlabel = QtWidgets.QLabel('%s:' % _('Boundary Margin'))
|
||||
@ -433,9 +485,9 @@ class GerberObjectUI(ObjectUI):
|
||||
"distance.")
|
||||
)
|
||||
bmlabel.setMinimumWidth(90)
|
||||
grid4.addWidget(bmlabel, 0, 0)
|
||||
self.noncopper_margin_entry = LengthEntry()
|
||||
grid4.addWidget(self.noncopper_margin_entry, 0, 1)
|
||||
grid2.addWidget(bmlabel, 4, 0)
|
||||
grid2.addWidget(self.noncopper_margin_entry, 4, 1)
|
||||
|
||||
# Rounded corners
|
||||
self.noncopper_rounded_cb = FCCheckBox(label=_("Rounded Geo"))
|
||||
@ -443,10 +495,10 @@ class GerberObjectUI(ObjectUI):
|
||||
_("Resulting geometry will have rounded corners.")
|
||||
)
|
||||
self.noncopper_rounded_cb.setMinimumWidth(90)
|
||||
grid4.addWidget(self.noncopper_rounded_cb, 1, 0)
|
||||
|
||||
self.generate_noncopper_button = QtWidgets.QPushButton(_('Generate Geo'))
|
||||
grid4.addWidget(self.generate_noncopper_button, 1, 1)
|
||||
grid2.addWidget(self.noncopper_rounded_cb, 5, 0)
|
||||
grid2.addWidget(self.generate_noncopper_button, 5, 1)
|
||||
|
||||
# ## Bounding box
|
||||
self.boundingbox_label = QtWidgets.QLabel('<b>%s:</b>' % _('Bounding Box'))
|
||||
@ -454,10 +506,8 @@ class GerberObjectUI(ObjectUI):
|
||||
_("Create a geometry surrounding the Gerber object.\n"
|
||||
"Square shape.")
|
||||
)
|
||||
self.custom_box.addWidget(self.boundingbox_label)
|
||||
|
||||
grid5 = QtWidgets.QGridLayout()
|
||||
self.custom_box.addLayout(grid5)
|
||||
grid2.addWidget(self.boundingbox_label, 6, 0, 1, 2)
|
||||
|
||||
bbmargin = QtWidgets.QLabel('%s:' % _('Boundary Margin'))
|
||||
bbmargin.setToolTip(
|
||||
@ -465,9 +515,9 @@ class GerberObjectUI(ObjectUI):
|
||||
"to the nearest polygon.")
|
||||
)
|
||||
bbmargin.setMinimumWidth(90)
|
||||
grid5.addWidget(bbmargin, 0, 0)
|
||||
self.bbmargin_entry = LengthEntry()
|
||||
grid5.addWidget(self.bbmargin_entry, 0, 1)
|
||||
grid2.addWidget(bbmargin, 7, 0)
|
||||
grid2.addWidget(self.bbmargin_entry, 7, 1)
|
||||
|
||||
self.bbrounded_cb = FCCheckBox(label=_("Rounded Geo"))
|
||||
self.bbrounded_cb.setToolTip(
|
||||
@ -477,13 +527,13 @@ class GerberObjectUI(ObjectUI):
|
||||
"the margin.")
|
||||
)
|
||||
self.bbrounded_cb.setMinimumWidth(90)
|
||||
grid5.addWidget(self.bbrounded_cb, 1, 0)
|
||||
|
||||
self.generate_bb_button = QtWidgets.QPushButton(_('Generate Geo'))
|
||||
self.generate_bb_button.setToolTip(
|
||||
_("Generate the Geometry object.")
|
||||
)
|
||||
grid5.addWidget(self.generate_bb_button, 1, 1)
|
||||
grid2.addWidget(self.bbrounded_cb, 8, 0)
|
||||
grid2.addWidget(self.generate_bb_button, 8, 1)
|
||||
|
||||
|
||||
class ExcellonObjectUI(ObjectUI):
|
||||
|
Loading…
Reference in New Issue
Block a user