- 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:
Marius Stanciu 2019-09-07 15:13:40 +03:00 committed by Marius
parent 2c2bdf5002
commit f164dae7a9
5 changed files with 294 additions and 122 deletions

View File

@ -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:

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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):