From c45444a77263f2abf86d996af06a2e0d934de51d Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Thu, 27 Aug 2020 21:37:00 +0300 Subject: [PATCH] - in Tools: Film, Image, InvertGerber, Optimal, PcbWizard - moved the Tool UI in its own class --- CHANGELOG.md | 1 + appTools/ToolFilm.py | 49 ++-- appTools/ToolImage.py | 302 +++++++++++--------- appTools/ToolInvertGerber.py | 315 ++++++++++---------- appTools/ToolOptimal.py | 538 ++++++++++++++++++----------------- appTools/ToolPcbWizard.py | 334 ++++++++++++---------- 6 files changed, 823 insertions(+), 716 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f1aa078..1a067405 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ CHANGELOG for FlatCAM beta - in Tool Cutout: modified the UI in preparation for adding the Mouse Bites feature - Turkish translation strings were updated by the translator, Mehmet Kaya - Film Tool - moved the Tool UI in its own class +- in Tools: Film, Image, InvertGerber, Optimal, PcbWizard - moved the Tool UI in its own class 26.08.2020 diff --git a/appTools/ToolFilm.py b/appTools/ToolFilm.py index ce62af61..c5831819 100644 --- a/appTools/ToolFilm.py +++ b/appTools/ToolFilm.py @@ -9,7 +9,7 @@ from PyQt5 import QtCore, QtWidgets, QtGui from appTool import AppTool from appGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, \ - OptionalHideInputSection, FCComboBox, FCFileSaveDialog + OptionalHideInputSection, FCComboBox, FCFileSaveDialog, FCButton, FCLabel from copy import deepcopy import logging @@ -759,7 +759,7 @@ class FilmUI: self.layout = layout # ## Title - title_label = QtWidgets.QLabel("%s" % self.toolName) + title_label = FCLabel("%s" % self.toolName) title_label.setStyleSheet(""" QLabel { @@ -768,7 +768,7 @@ class FilmUI: } """) self.layout.addWidget(title_label) - self.layout.addWidget(QtWidgets.QLabel("")) + self.layout.addWidget(FCLabel("")) # Form Layout grid0 = QtWidgets.QGridLayout() @@ -781,7 +781,7 @@ class FilmUI: self.tf_type_obj_combo = RadioSet([{'label': _('Gerber'), 'value': 'grb'}, {'label': _('Geometry'), 'value': 'geo'}]) - self.tf_type_obj_combo_label = QtWidgets.QLabel('%s:' % _("Object")) + self.tf_type_obj_combo_label = FCLabel('%s:' % _("Object")) self.tf_type_obj_combo_label.setToolTip( _("Specify the type of object for which to create the film.\n" "The object can be of type: Gerber or Geometry.\n" @@ -804,7 +804,7 @@ class FilmUI: self.tf_type_box_combo = RadioSet([{'label': _('Gerber'), 'value': 'grb'}, {'label': _('Geometry'), 'value': 'geo'}]) - self.tf_type_box_combo_label = QtWidgets.QLabel(_("Box Type:")) + self.tf_type_box_combo_label = FCLabel(_("Box Type:")) self.tf_type_box_combo_label.setToolTip( _("Specify the type of object to be used as an container for\n" "film creation. It can be: Gerber or Geometry type." @@ -827,7 +827,7 @@ class FilmUI: separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) grid0.addWidget(separator_line, 4, 0, 1, 2) - self.film_adj_label = QtWidgets.QLabel('%s' % _("Film Adjustments")) + self.film_adj_label = FCLabel('%s' % _("Film Adjustments")) self.film_adj_label.setToolTip( _("Sometime the printers will distort the print shape, especially the Laser types.\n" "This section provide the tools to compensate for the print distortions.") @@ -848,7 +848,7 @@ class FilmUI: ) grid0.addWidget(self.film_scale_cb, 6, 0, 1, 2) - self.film_scalex_label = QtWidgets.QLabel('%s:' % _("X factor")) + self.film_scalex_label = FCLabel('%s:' % _("X factor")) self.film_scalex_entry = FCDoubleSpinner(callback=self.confirmation_message) self.film_scalex_entry.set_range(-999.9999, 999.9999) self.film_scalex_entry.set_precision(self.decimals) @@ -857,7 +857,7 @@ class FilmUI: grid0.addWidget(self.film_scalex_label, 7, 0) grid0.addWidget(self.film_scalex_entry, 7, 1) - self.film_scaley_label = QtWidgets.QLabel('%s:' % _("Y factor")) + self.film_scaley_label = FCLabel('%s:' % _("Y factor")) self.film_scaley_entry = FCDoubleSpinner(callback=self.confirmation_message) self.film_scaley_entry.set_range(-999.9999, 999.9999) self.film_scaley_entry.set_precision(self.decimals) @@ -892,7 +892,7 @@ class FilmUI: ) grid0.addWidget(self.film_skew_cb, 10, 0, 1, 2) - self.film_skewx_label = QtWidgets.QLabel('%s:' % _("X angle")) + self.film_skewx_label = FCLabel('%s:' % _("X angle")) self.film_skewx_entry = FCDoubleSpinner(callback=self.confirmation_message) self.film_skewx_entry.set_range(-999.9999, 999.9999) self.film_skewx_entry.set_precision(self.decimals) @@ -901,7 +901,7 @@ class FilmUI: grid0.addWidget(self.film_skewx_label, 11, 0) grid0.addWidget(self.film_skewx_entry, 11, 1) - self.film_skewy_label = QtWidgets.QLabel('%s:' % _("Y angle")) + self.film_skewy_label = FCLabel('%s:' % _("Y angle")) self.film_skewy_entry = FCDoubleSpinner(callback=self.confirmation_message) self.film_skewy_entry.set_range(-999.9999, 999.9999) self.film_skewy_entry.set_precision(self.decimals) @@ -910,7 +910,7 @@ class FilmUI: grid0.addWidget(self.film_skewy_label, 12, 0) grid0.addWidget(self.film_skewy_entry, 12, 1) - self.film_skew_ref_label = QtWidgets.QLabel('%s:' % _("Reference")) + self.film_skew_ref_label = FCLabel('%s:' % _("Reference")) self.film_skew_ref_label.setToolTip( _("The reference point to be used as origin for the skew.\n" "It can be one of the four points of the geometry bounding box.") @@ -957,7 +957,7 @@ class FilmUI: {'label': _('Y'), 'value': 'y'}, {'label': _('Both'), 'value': 'both'}], stretch=False) - self.film_mirror_axis_label = QtWidgets.QLabel('%s:' % _("Mirror axis")) + self.film_mirror_axis_label = FCLabel('%s:' % _("Mirror axis")) grid0.addWidget(self.film_mirror_axis_label, 16, 0) grid0.addWidget(self.film_mirror_axis, 16, 1) @@ -973,7 +973,7 @@ class FilmUI: separator_line2.setFrameShadow(QtWidgets.QFrame.Sunken) grid0.addWidget(separator_line2, 17, 0, 1, 2) - self.film_param_label = QtWidgets.QLabel('%s' % _("Film Parameters")) + self.film_param_label = FCLabel('%s' % _("Film Parameters")) grid0.addWidget(self.film_param_label, 18, 0, 1, 2) @@ -983,7 +983,7 @@ class FilmUI: self.film_scale_stroke_entry.setSingleStep(0.01) self.film_scale_stroke_entry.set_precision(self.decimals) - self.film_scale_stroke_label = QtWidgets.QLabel('%s:' % _("Scale Stroke")) + self.film_scale_stroke_label = FCLabel('%s:' % _("Scale Stroke")) self.film_scale_stroke_label.setToolTip( _("Scale the line stroke thickness of each feature in the SVG file.\n" "It means that the line that envelope each SVG feature will be thicker or thinner,\n" @@ -996,7 +996,7 @@ class FilmUI: self.film_type = RadioSet([{'label': _('Positive'), 'value': 'pos'}, {'label': _('Negative'), 'value': 'neg'}], stretch=False) - self.film_type_label = QtWidgets.QLabel(_("Film Type:")) + self.film_type_label = FCLabel(_("Film Type:")) self.film_type_label.setToolTip( _("Generate a Positive black film or a Negative film.\n" "Positive means that it will print the features\n" @@ -1014,7 +1014,7 @@ class FilmUI: self.boundary_entry.setSingleStep(0.01) self.boundary_entry.set_precision(self.decimals) - self.boundary_label = QtWidgets.QLabel('%s:' % _("Border")) + self.boundary_label = FCLabel('%s:' % _("Border")) self.boundary_label.setToolTip( _("Specify a border around the object.\n" "Only for negative film.\n" @@ -1051,7 +1051,7 @@ class FilmUI: self.ois_p = OptionalHideInputSection(self.punch_cb, [self.punch_frame]) - self.source_label = QtWidgets.QLabel('%s:' % _("Source")) + self.source_label = FCLabel('%s:' % _("Source")) self.source_label.setToolTip( _("The punch hole source can be:\n" "- Excellon -> an Excellon holes center will serve as reference.\n" @@ -1063,7 +1063,7 @@ class FilmUI: punch_grid.addWidget(self.source_label, 0, 0) punch_grid.addWidget(self.source_punch, 0, 1) - self.exc_label = QtWidgets.QLabel('%s:' % _("Excellon Obj")) + self.exc_label = FCLabel('%s:' % _("Excellon Obj")) self.exc_label.setToolTip( _("Remove the geometry of Excellon from the Film to create the holes in pads.") ) @@ -1079,7 +1079,7 @@ class FilmUI: self.exc_label.hide() self.exc_combo.hide() - self.punch_size_label = QtWidgets.QLabel('%s:' % _("Punch Size")) + self.punch_size_label = FCLabel('%s:' % _("Punch Size")) self.punch_size_label.setToolTip(_("The value here will control how big is the punch hole in the pads.")) self.punch_size_spinner = FCDoubleSpinner(callback=self.confirmation_message) self.punch_size_spinner.set_range(0, 999.9999) @@ -1108,7 +1108,7 @@ class FilmUI: {'label': _('PDF'), 'value': 'pdf'} ], stretch=False) - self.file_type_label = QtWidgets.QLabel(_("Film Type:")) + self.file_type_label = FCLabel(_("Film Type:")) self.file_type_label.setToolTip( _("The file type of the saved film. Can be:\n" "- 'SVG' -> open-source vectorial format\n" @@ -1119,7 +1119,7 @@ class FilmUI: grid1.addWidget(self.file_type_radio, 1, 1) # Page orientation - self.orientation_label = QtWidgets.QLabel('%s:' % _("Page Orientation")) + self.orientation_label = FCLabel('%s:' % _("Page Orientation")) self.orientation_label.setToolTip(_("Can be:\n" "- Portrait\n" "- Landscape")) @@ -1132,7 +1132,7 @@ class FilmUI: grid1.addWidget(self.orientation_radio, 2, 1) # Page Size - self.pagesize_label = QtWidgets.QLabel('%s:' % _("Page Size")) + self.pagesize_label = FCLabel('%s:' % _("Page Size")) self.pagesize_label.setToolTip(_("A selection of standard ISO 216 page sizes.")) self.pagesize_combo = FCComboBox() @@ -1200,7 +1200,8 @@ class FilmUI: self.on_film_type(val='hide') # Buttons - self.film_object_button = QtWidgets.QPushButton(_("Save Film")) + self.film_object_button = FCButton(_("Save Film")) + self.film_object_button.setIcon(QtGui.QIcon(self.app.resource_location + '/save_as.png')) self.film_object_button.setToolTip( _("Create a Film for the selected object, within\n" "the specified box. Does not create a new \n " @@ -1218,7 +1219,7 @@ class FilmUI: self.layout.addStretch() # ## Reset Tool - self.reset_button = QtWidgets.QPushButton(_("Reset Tool")) + self.reset_button = FCButton(_("Reset Tool")) self.reset_button.setIcon(QtGui.QIcon(self.app.resource_location + '/reset32.png')) self.reset_button.setToolTip( _("Will reset the tool parameters.") diff --git a/appTools/ToolImage.py b/appTools/ToolImage.py index f29f1267..12728ff8 100644 --- a/appTools/ToolImage.py +++ b/appTools/ToolImage.py @@ -21,23 +21,165 @@ if '_' not in builtins.__dict__: class ToolImage(AppTool): - toolName = _("Image as Object") - def __init__(self, app): AppTool.__init__(self, app) self.app = app self.decimals = self.app.decimals - # Title - title_label = QtWidgets.QLabel("%s" % _('Image to PCB')) + # ############################################################################# + # ######################### Tool GUI ########################################## + # ############################################################################# + self.ui = ImageUI(layout=self.layout, app=self.app) + self.toolName = self.ui.toolName + + # ## Signals + self.ui.import_button.clicked.connect(self.on_file_importimage) + self.ui.image_type.activated_custom.connect(self.ui.on_image_type) + + def run(self, toggle=True): + self.app.defaults.report_usage("ToolImage()") + + if toggle: + # if the splitter is hidden, display it, else hide it but only if the current widget is the same + if self.app.ui.splitter.sizes()[0] == 0: + self.app.ui.splitter.setSizes([1, 1]) + else: + try: + if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName: + # if tab is populated with the tool but it does not have the focus, focus on it + if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab: + # focus on Tool Tab + self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab) + else: + self.app.ui.splitter.setSizes([0, 1]) + except AttributeError: + pass + else: + if self.app.ui.splitter.sizes()[0] == 0: + self.app.ui.splitter.setSizes([1, 1]) + + AppTool.run(self) + self.set_tool_ui() + + self.app.ui.notebook.setTabText(2, _("Image Tool")) + + def install(self, icon=None, separator=None, **kwargs): + AppTool.install(self, icon, separator, **kwargs) + + def set_tool_ui(self): + # ## Initialize form + self.ui.dpi_entry.set_value(96) + self.ui.image_type.set_value('black') + self.ui.mask_bw_entry.set_value(250) + self.ui.mask_r_entry.set_value(250) + self.ui.mask_g_entry.set_value(250) + self.ui.mask_b_entry.set_value(250) + + def on_file_importimage(self): + """ + Callback for menu item File->Import IMAGE. + :param type_of_obj: to import the IMAGE as Geometry or as Gerber + :type type_of_obj: str + :return: None + """ + mask = [] + self.app.log.debug("on_file_importimage()") + + _filter = "Image Files(*.BMP *.PNG *.JPG *.JPEG);;" \ + "Bitmap File (*.BMP);;" \ + "PNG File (*.PNG);;" \ + "Jpeg File (*.JPG);;" \ + "All Files (*.*)" + try: + filename, _f = QtWidgets.QFileDialog.getOpenFileName(caption=_("Import IMAGE"), + directory=self.app.get_last_folder(), filter=_filter) + except TypeError: + filename, _f = QtWidgets.QFileDialog.getOpenFileName(caption=_("Import IMAGE"), filter=filter) + + filename = str(filename) + type_obj = self.ui.tf_type_obj_combo.get_value() + dpi = self.ui.dpi_entry.get_value() + mode = self.ui.image_type.get_value() + mask = [ + self.ui.mask_bw_entry.get_value(), + self.ui.mask_r_entry.get_value(), + self.ui.mask_g_entry.get_value(), + self.ui.mask_b_entry.get_value() + ] + + if filename == "": + self.app.inform.emit(_("Cancelled.")) + else: + self.app.worker_task.emit({'fcn': self.import_image, + 'params': [filename, type_obj, dpi, mode, mask]}) + + def import_image(self, filename, o_type=_("Gerber"), dpi=96, mode='black', mask=None, outname=None): + """ + Adds a new Geometry Object to the projects and populates + it with shapes extracted from the SVG file. + + :param filename: Path to the SVG file. + :param o_type: type of FlatCAM objeect + :param dpi: dot per inch + :param mode: black or color + :param mask: dictate the level of detail + :param outname: name for the resulting file + :return: + """ + + self.app.defaults.report_usage("import_image()") + + if mask is None: + mask = [250, 250, 250, 250] + + if o_type is None or o_type == _("Geometry"): + obj_type = "geometry" + elif o_type == _("Gerber"): + obj_type = "gerber" + else: + self.app.inform.emit('[ERROR_NOTCL] %s' % + _("Not supported type is picked as parameter. " + "Only Geometry and Gerber are supported")) + return + + def obj_init(geo_obj, app_obj): + geo_obj.import_image(filename, units=units, dpi=dpi, mode=mode, mask=mask) + geo_obj.multigeo = False + + with self.app.proc_container.new(_("Importing Image")) as proc: + + # Object name + name = outname or filename.split('/')[-1].split('\\')[-1] + units = self.app.defaults['units'] + + self.app.app_obj.new_object(obj_type, name, obj_init) + + # Register recent file + self.app.file_opened.emit("image", filename) + + # GUI feedback + self.app.inform.emit('[success] %s: %s' % (_("Opened"), filename)) + + +class ImageUI: + + toolName = _("Image as Object") + + def __init__(self, layout, app): + self.app = app + self.decimals = self.app.decimals + self.layout = layout + + # ## Title + title_label = QtWidgets.QLabel("%s" % self.toolName) title_label.setStyleSheet(""" - QLabel - { - font-size: 16px; - font-weight: bold; - } - """) + QLabel + { + font-size: 16px; + font-weight: bold; + } + """) self.layout.addWidget(title_label) # Form Layout @@ -53,8 +195,8 @@ class ToolImage(AppTool): self.tf_type_obj_combo_label = QtWidgets.QLabel('%s:' % _("Object Type")) self.tf_type_obj_combo_label.setToolTip( - _("Specify the type of object to create from the image.\n" - "It can be of type: Gerber or Geometry.") + _("Specify the type of object to create from the image.\n" + "It can be of type: Gerber or Geometry.") ) ti_form_layout.addRow(self.tf_type_obj_combo_label, self.tf_type_obj_combo) @@ -63,7 +205,7 @@ class ToolImage(AppTool): self.dpi_entry = FCSpinner(callback=self.confirmation_message_int) self.dpi_entry.set_range(0, 99999) self.dpi_label = QtWidgets.QLabel('%s:' % _("DPI value")) - self.dpi_label.setToolTip(_("Specify a DPI value for the image.") ) + self.dpi_label.setToolTip(_("Specify a DPI value for the image.")) ti_form_layout.addRow(self.dpi_label, self.dpi_entry) self.emty_lbl = QtWidgets.QLabel("") @@ -150,48 +292,8 @@ class ToolImage(AppTool): self.on_image_type(val=False) - # ## Signals - self.import_button.clicked.connect(self.on_file_importimage) - self.image_type.activated_custom.connect(self.on_image_type) - - def run(self, toggle=True): - self.app.defaults.report_usage("ToolImage()") - - if toggle: - # if the splitter is hidden, display it, else hide it but only if the current widget is the same - if self.app.ui.splitter.sizes()[0] == 0: - self.app.ui.splitter.setSizes([1, 1]) - else: - try: - if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName: - # if tab is populated with the tool but it does not have the focus, focus on it - if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab: - # focus on Tool Tab - self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab) - else: - self.app.ui.splitter.setSizes([0, 1]) - except AttributeError: - pass - else: - if self.app.ui.splitter.sizes()[0] == 0: - self.app.ui.splitter.setSizes([1, 1]) - - AppTool.run(self) - self.set_tool_ui() - - self.app.ui.notebook.setTabText(2, _("Image Tool")) - - def install(self, icon=None, separator=None, **kwargs): - AppTool.install(self, icon, separator, **kwargs) - - def set_tool_ui(self): - # ## Initialize form - self.dpi_entry.set_value(96) - self.image_type.set_value('black') - self.mask_bw_entry.set_value(250) - self.mask_r_entry.set_value(250) - self.mask_g_entry.set_value(250) - self.mask_b_entry.set_value(250) + # #################################### FINSIHED GUI ########################### + # ############################################################################# def on_image_type(self, val): if val == 'color': @@ -215,83 +317,19 @@ class ToolImage(AppTool): self.mask_bw_label.setDisabled(False) self.mask_bw_entry.setDisabled(False) - def on_file_importimage(self): - """ - Callback for menu item File->Import IMAGE. - :param type_of_obj: to import the IMAGE as Geometry or as Gerber - :type type_of_obj: str - :return: None - """ - mask = [] - self.app.log.debug("on_file_importimage()") - - _filter = "Image Files(*.BMP *.PNG *.JPG *.JPEG);;" \ - "Bitmap File (*.BMP);;" \ - "PNG File (*.PNG);;" \ - "Jpeg File (*.JPG);;" \ - "All Files (*.*)" - try: - filename, _f = QtWidgets.QFileDialog.getOpenFileName(caption=_("Import IMAGE"), - directory=self.app.get_last_folder(), filter=_filter) - except TypeError: - filename, _f = QtWidgets.QFileDialog.getOpenFileName(caption=_("Import IMAGE"), filter=filter) - - filename = str(filename) - type_obj = self.tf_type_obj_combo.get_value() - dpi = self.dpi_entry.get_value() - mode = self.image_type.get_value() - mask = [self.mask_bw_entry.get_value(), self.mask_r_entry.get_value(), self.mask_g_entry.get_value(), - self.mask_b_entry.get_value()] - - if filename == "": - self.app.inform.emit(_("Cancelled.")) + def confirmation_message(self, accepted, minval, maxval): + if accepted is False: + self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' % (_("Edited value is out of range"), + self.decimals, + minval, + self.decimals, + maxval), False) else: - self.app.worker_task.emit({'fcn': self.import_image, - 'params': [filename, type_obj, dpi, mode, mask]}) + self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False) - def import_image(self, filename, o_type=_("Gerber"), dpi=96, mode='black', mask=None, outname=None): - """ - Adds a new Geometry Object to the projects and populates - it with shapes extracted from the SVG file. - - :param filename: Path to the SVG file. - :param o_type: type of FlatCAM objeect - :param dpi: dot per inch - :param mode: black or color - :param mask: dictate the level of detail - :param outname: name for the resulting file - :return: - """ - - self.app.defaults.report_usage("import_image()") - - if mask is None: - mask = [250, 250, 250, 250] - - if o_type is None or o_type == _("Geometry"): - obj_type = "geometry" - elif o_type == _("Gerber"): - obj_type = "gerber" + def confirmation_message_int(self, accepted, minval, maxval): + if accepted is False: + self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' % + (_("Edited value is out of range"), minval, maxval), False) else: - self.app.inform.emit('[ERROR_NOTCL] %s' % - _("Not supported type is picked as parameter. " - "Only Geometry and Gerber are supported")) - return - - def obj_init(geo_obj, app_obj): - geo_obj.import_image(filename, units=units, dpi=dpi, mode=mode, mask=mask) - geo_obj.multigeo = False - - with self.app.proc_container.new(_("Importing Image")) as proc: - - # Object name - name = outname or filename.split('/')[-1].split('\\')[-1] - units = self.app.defaults['units'] - - self.app.app_obj.new_object(obj_type, name, obj_init) - - # Register recent file - self.app.file_opened.emit("image", filename) - - # GUI feedback - self.app.inform.emit('[success] %s: %s' % (_("Opened"), filename)) + self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False) diff --git a/appTools/ToolInvertGerber.py b/appTools/ToolInvertGerber.py index 985da1f8..395acc12 100644 --- a/appTools/ToolInvertGerber.py +++ b/appTools/ToolInvertGerber.py @@ -8,7 +8,7 @@ from PyQt5 import QtWidgets, QtCore, QtGui from appTool import AppTool -from appGUI.GUIElements import FCButton, FCDoubleSpinner, RadioSet, FCComboBox +from appGUI.GUIElements import FCButton, FCDoubleSpinner, RadioSet, FCComboBox, FCLabel from shapely.geometry import box @@ -28,133 +28,20 @@ log = logging.getLogger('base') class ToolInvertGerber(AppTool): - toolName = _("Invert Gerber Tool") - def __init__(self, app): self.app = app self.decimals = self.app.decimals AppTool.__init__(self, app) - self.tools_frame = QtWidgets.QFrame() - self.tools_frame.setContentsMargins(0, 0, 0, 0) - self.layout.addWidget(self.tools_frame) - self.tools_box = QtWidgets.QVBoxLayout() - self.tools_box.setContentsMargins(0, 0, 0, 0) - self.tools_frame.setLayout(self.tools_box) + # ############################################################################# + # ######################### Tool GUI ########################################## + # ############################################################################# + self.ui = InvertUI(layout=self.layout, app=self.app) + self.toolName = self.ui.toolName - # Title - title_label = QtWidgets.QLabel("%s" % self.toolName) - title_label.setStyleSheet(""" - QLabel - { - font-size: 16px; - font-weight: bold; - } - """) - self.tools_box.addWidget(title_label) - - # Grid Layout - grid0 = QtWidgets.QGridLayout() - grid0.setColumnStretch(0, 0) - grid0.setColumnStretch(1, 1) - self.tools_box.addLayout(grid0) - - grid0.addWidget(QtWidgets.QLabel(''), 0, 0, 1, 2) - - # Target Gerber Object - self.gerber_combo = FCComboBox() - self.gerber_combo.setModel(self.app.collection) - self.gerber_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) - self.gerber_combo.is_last = True - self.gerber_combo.obj_type = "Gerber" - - self.gerber_label = QtWidgets.QLabel('%s:' % _("GERBER")) - self.gerber_label.setToolTip( - _("Gerber object that will be inverted.") - ) - - grid0.addWidget(self.gerber_label, 1, 0, 1, 2) - grid0.addWidget(self.gerber_combo, 2, 0, 1, 2) - - separator_line = QtWidgets.QFrame() - separator_line.setFrameShape(QtWidgets.QFrame.HLine) - separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) - grid0.addWidget(separator_line, 3, 0, 1, 2) - - self.param_label = QtWidgets.QLabel("%s:" % _("Parameters")) - self.param_label.setToolTip('%s.' % _("Parameters for this tool")) - - grid0.addWidget(self.param_label, 4, 0, 1, 2) - - # Margin - self.margin_label = QtWidgets.QLabel('%s:' % _('Margin')) - self.margin_label.setToolTip( - _("Distance by which to avoid\n" - "the edges of the Gerber object.") - ) - self.margin_entry = FCDoubleSpinner(callback=self.confirmation_message) - self.margin_entry.set_precision(self.decimals) - self.margin_entry.set_range(0.0000, 9999.9999) - self.margin_entry.setObjectName(_("Margin")) - - grid0.addWidget(self.margin_label, 5, 0, 1, 2) - grid0.addWidget(self.margin_entry, 6, 0, 1, 2) - - self.join_label = QtWidgets.QLabel('%s:' % _("Lines Join Style")) - self.join_label.setToolTip( - _("The way that the lines in the object outline will be joined.\n" - "Can be:\n" - "- rounded -> an arc is added between two joining lines\n" - "- square -> the lines meet in 90 degrees angle\n" - "- bevel -> the lines are joined by a third line") - ) - self.join_radio = RadioSet([ - {'label': 'Rounded', 'value': 'r'}, - {'label': 'Square', 'value': 's'}, - {'label': 'Bevel', 'value': 'b'} - ], orientation='vertical', stretch=False) - - grid0.addWidget(self.join_label, 7, 0, 1, 2) - grid0.addWidget(self.join_radio, 8, 0, 1, 2) - - separator_line = QtWidgets.QFrame() - separator_line.setFrameShape(QtWidgets.QFrame.HLine) - separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) - grid0.addWidget(separator_line, 9, 0, 1, 2) - - self.invert_btn = FCButton(_('Invert Gerber')) - self.invert_btn.setToolTip( - _("Will invert the Gerber object: areas that have copper\n" - "will be empty of copper and previous empty area will be\n" - "filled with copper.") - ) - self.invert_btn.setStyleSheet(""" - QPushButton - { - font-weight: bold; - } - """) - grid0.addWidget(self.invert_btn, 10, 0, 1, 2) - - self.tools_box.addStretch() - - # ## Reset Tool - self.reset_button = QtWidgets.QPushButton(_("Reset Tool")) - self.reset_button.setIcon(QtGui.QIcon(self.app.resource_location + '/reset32.png')) - self.reset_button.setToolTip( - _("Will reset the tool parameters.") - ) - self.reset_button.setStyleSheet(""" - QPushButton - { - font-weight: bold; - } - """) - self.tools_box.addWidget(self.reset_button) - - self.invert_btn.clicked.connect(self.on_grb_invert) - self.reset_button.clicked.connect(self.set_tool_ui) + self.ui.invert_btn.clicked.connect(self.on_grb_invert) + self.ui.reset_button.clicked.connect(self.set_tool_ui) def install(self, icon=None, separator=None, **kwargs): AppTool.install(self, icon, separator, shortcut='', **kwargs) @@ -188,20 +75,20 @@ class ToolInvertGerber(AppTool): self.app.ui.notebook.setTabText(2, _("Invert Tool")) def set_tool_ui(self): - self.margin_entry.set_value(float(self.app.defaults["tools_invert_margin"])) - self.join_radio.set_value(self.app.defaults["tools_invert_join_style"]) + self.ui.margin_entry.set_value(float(self.app.defaults["tools_invert_margin"])) + self.ui.join_radio.set_value(self.app.defaults["tools_invert_join_style"]) def on_grb_invert(self): - margin = self.margin_entry.get_value() + margin = self.ui.margin_entry.get_value() if round(margin, self.decimals) == 0.0: margin = 1E-10 - join_style = {'r': 1, 'b': 3, 's': 2}[self.join_radio.get_value()] + join_style = {'r': 1, 'b': 3, 's': 2}[self.ui.join_radio.get_value()] if join_style is None: join_style = 'r' grb_circle_steps = int(self.app.defaults["gerber_circle_steps"]) - obj_name = self.gerber_combo.currentText() + obj_name = self.ui.gerber_combo.currentText() outname = obj_name + "_inverted" @@ -243,32 +130,6 @@ class ToolInvertGerber(AppTool): new_apertures = {} - # for apid, val in grb_obj.apertures.items(): - # new_apertures[apid] = {} - # for key in val: - # if key == 'geometry': - # new_apertures[apid]['geometry'] = [] - # for elem in val['geometry']: - # geo_elem = {} - # if 'follow' in elem: - # try: - # geo_elem['clear'] = elem['follow'].buffer(val['size'] / 2.0).exterior - # except AttributeError: - # # TODO should test if width or height is bigger - # geo_elem['clear'] = elem['follow'].buffer(val['width'] / 2.0).exterior - # if 'clear' in elem: - # if isinstance(elem['clear'], Polygon): - # try: - # geo_elem['solid'] = elem['clear'].buffer(val['size'] / 2.0, grb_circle_steps) - # except AttributeError: - # # TODO should test if width or height is bigger - # geo_elem['solid'] = elem['clear'].buffer(val['width'] / 2.0, grb_circle_steps) - # else: - # geo_elem['follow'] = elem['clear'] - # new_apertures[apid]['geometry'].append(deepcopy(geo_elem)) - # else: - # new_apertures[apid][key] = deepcopy(val[key]) - if '0' not in new_apertures: new_apertures['0'] = {} new_apertures['0']['type'] = 'C' @@ -302,9 +163,155 @@ class ToolInvertGerber(AppTool): self.app.app_obj.new_object('gerber', outname, init_func) def reset_fields(self): - self.gerber_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) + self.ui.gerber_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) @staticmethod def poly2rings(poly): return [poly.exterior] + [interior for interior in poly.interiors] -# end of file + + +class InvertUI: + + toolName = _("Invert Gerber Tool") + + def __init__(self, layout, app): + self.app = app + self.decimals = self.app.decimals + self.layout = layout + + # ## Title + title_label = FCLabel("%s" % self.toolName) + title_label.setStyleSheet(""" + QLabel + { + font-size: 16px; + font-weight: bold; + } + """) + self.layout.addWidget(title_label) + self.layout.addWidget(FCLabel("")) + + self.tools_frame = QtWidgets.QFrame() + self.tools_frame.setContentsMargins(0, 0, 0, 0) + self.layout.addWidget(self.tools_frame) + + self.tools_box = QtWidgets.QVBoxLayout() + self.tools_box.setContentsMargins(0, 0, 0, 0) + self.tools_frame.setLayout(self.tools_box) + + # Grid Layout + grid0 = QtWidgets.QGridLayout() + grid0.setColumnStretch(0, 0) + grid0.setColumnStretch(1, 1) + self.tools_box.addLayout(grid0) + + # Target Gerber Object + self.gerber_combo = FCComboBox() + self.gerber_combo.setModel(self.app.collection) + self.gerber_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) + self.gerber_combo.is_last = True + self.gerber_combo.obj_type = "Gerber" + + self.gerber_label = FCLabel('%s:' % _("GERBER")) + self.gerber_label.setToolTip( + _("Gerber object that will be inverted.") + ) + + grid0.addWidget(self.gerber_label, 1, 0, 1, 2) + grid0.addWidget(self.gerber_combo, 2, 0, 1, 2) + + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + grid0.addWidget(separator_line, 3, 0, 1, 2) + + self.param_label = FCLabel("%s:" % _("Parameters")) + self.param_label.setToolTip('%s.' % _("Parameters for this tool")) + + grid0.addWidget(self.param_label, 4, 0, 1, 2) + + # Margin + self.margin_label = FCLabel('%s:' % _('Margin')) + self.margin_label.setToolTip( + _("Distance by which to avoid\n" + "the edges of the Gerber object.") + ) + self.margin_entry = FCDoubleSpinner(callback=self.confirmation_message) + self.margin_entry.set_precision(self.decimals) + self.margin_entry.set_range(0.0000, 9999.9999) + self.margin_entry.setObjectName(_("Margin")) + + grid0.addWidget(self.margin_label, 5, 0, 1, 2) + grid0.addWidget(self.margin_entry, 6, 0, 1, 2) + + self.join_label = FCLabel('%s:' % _("Lines Join Style")) + self.join_label.setToolTip( + _("The way that the lines in the object outline will be joined.\n" + "Can be:\n" + "- rounded -> an arc is added between two joining lines\n" + "- square -> the lines meet in 90 degrees angle\n" + "- bevel -> the lines are joined by a third line") + ) + self.join_radio = RadioSet([ + {'label': 'Rounded', 'value': 'r'}, + {'label': 'Square', 'value': 's'}, + {'label': 'Bevel', 'value': 'b'} + ], orientation='vertical', stretch=False) + + grid0.addWidget(self.join_label, 7, 0, 1, 2) + grid0.addWidget(self.join_radio, 8, 0, 1, 2) + + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + grid0.addWidget(separator_line, 9, 0, 1, 2) + + self.invert_btn = FCButton(_('Invert Gerber')) + self.invert_btn.setToolTip( + _("Will invert the Gerber object: areas that have copper\n" + "will be empty of copper and previous empty area will be\n" + "filled with copper.") + ) + self.invert_btn.setStyleSheet(""" + QPushButton + { + font-weight: bold; + } + """) + grid0.addWidget(self.invert_btn, 10, 0, 1, 2) + + self.tools_box.addStretch() + + # ## Reset Tool + self.reset_button = FCButton(_("Reset Tool")) + self.reset_button.setIcon(QtGui.QIcon(self.app.resource_location + '/reset32.png')) + self.reset_button.setToolTip( + _("Will reset the tool parameters.") + ) + self.reset_button.setStyleSheet(""" + QPushButton + { + font-weight: bold; + } + """) + self.tools_box.addWidget(self.reset_button) + + # #################################### FINSIHED GUI ########################### + # ############################################################################# + + def confirmation_message(self, accepted, minval, maxval): + if accepted is False: + self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' % (_("Edited value is out of range"), + self.decimals, + minval, + self.decimals, + maxval), False) + else: + self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False) + + def confirmation_message_int(self, accepted, minval, maxval): + if accepted is False: + self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' % + (_("Edited value is out of range"), minval, maxval), False) + else: + self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False) \ No newline at end of file diff --git a/appTools/ToolOptimal.py b/appTools/ToolOptimal.py index b6582210..b17a8e63 100644 --- a/appTools/ToolOptimal.py +++ b/appTools/ToolOptimal.py @@ -30,8 +30,6 @@ log = logging.getLogger('base') class ToolOptimal(AppTool): - toolName = _("Optimal Tool") - update_text = QtCore.pyqtSignal(list) update_sec_distances = QtCore.pyqtSignal(dict) @@ -41,222 +39,11 @@ class ToolOptimal(AppTool): self.units = self.app.defaults['units'].upper() self.decimals = self.app.decimals - # ############################################################################ - # ############################ GUI creation ################################## - # ## Title - title_label = QtWidgets.QLabel("%s" % self.toolName) - title_label.setStyleSheet( - """ - QLabel - { - font-size: 16px; - font-weight: bold; - } - """) - self.layout.addWidget(title_label) - - # ## Form Layout - form_lay = QtWidgets.QFormLayout() - self.layout.addLayout(form_lay) - - form_lay.addRow(QtWidgets.QLabel("")) - - # ## Gerber Object to mirror - self.gerber_object_combo = FCComboBox() - self.gerber_object_combo.setModel(self.app.collection) - self.gerber_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) - self.gerber_object_combo.is_last = True - self.gerber_object_combo.obj_type = "Gerber" - - self.gerber_object_label = QtWidgets.QLabel("%s:" % _("GERBER")) - self.gerber_object_label.setToolTip( - "Gerber object for which to find the minimum distance between copper features." - ) - form_lay.addRow(self.gerber_object_label) - form_lay.addRow(self.gerber_object_combo) - - separator_line = QtWidgets.QFrame() - separator_line.setFrameShape(QtWidgets.QFrame.HLine) - separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) - form_lay.addRow(separator_line) - - # Precision = nr of decimals - self.precision_label = QtWidgets.QLabel('%s:' % _("Precision")) - self.precision_label.setToolTip(_("Number of decimals kept for found distances.")) - - self.precision_spinner = FCSpinner(callback=self.confirmation_message_int) - self.precision_spinner.set_range(2, 10) - self.precision_spinner.setWrapping(True) - form_lay.addRow(self.precision_label, self.precision_spinner) - - # Results Title - self.title_res_label = QtWidgets.QLabel('%s:' % _("Minimum distance")) - self.title_res_label.setToolTip(_("Display minimum distance between copper features.")) - form_lay.addRow(self.title_res_label) - - # Result value - self.result_label = QtWidgets.QLabel('%s:' % _("Determined")) - self.result_entry = FCEntry() - self.result_entry.setReadOnly(True) - - self.units_lbl = QtWidgets.QLabel(self.units.lower()) - self.units_lbl.setDisabled(True) - - hlay = QtWidgets.QHBoxLayout() - hlay.addWidget(self.result_entry) - hlay.addWidget(self.units_lbl) - - form_lay.addRow(self.result_label, hlay) - - # Frequency of minimum encounter - self.freq_label = QtWidgets.QLabel('%s:' % _("Occurring")) - self.freq_label.setToolTip(_("How many times this minimum is found.")) - self.freq_entry = FCEntry() - self.freq_entry.setReadOnly(True) - form_lay.addRow(self.freq_label, self.freq_entry) - - # Control if to display the locations of where the minimum was found - self.locations_cb = FCCheckBox(_("Minimum points coordinates")) - self.locations_cb.setToolTip(_("Coordinates for points where minimum distance was found.")) - form_lay.addRow(self.locations_cb) - - # Locations where minimum was found - self.locations_textb = FCTextArea(parent=self) - self.locations_textb.setPlaceholderText( - _("Coordinates for points where minimum distance was found.") - ) - self.locations_textb.setReadOnly(True) - stylesheet = """ - QTextEdit { selection-background-color:blue; - selection-color:white; - } - """ - - self.locations_textb.setStyleSheet(stylesheet) - form_lay.addRow(self.locations_textb) - - # Jump button - self.locate_button = QtWidgets.QPushButton(_("Jump to selected position")) - self.locate_button.setToolTip( - _("Select a position in the Locations text box and then\n" - "click this button.") - ) - self.locate_button.setMinimumWidth(60) - self.locate_button.setDisabled(True) - form_lay.addRow(self.locate_button) - - # Other distances in Gerber - self.title_second_res_label = QtWidgets.QLabel('%s:' % _("Other distances")) - self.title_second_res_label.setToolTip(_("Will display other distances in the Gerber file ordered from\n" - "the minimum to the maximum, not including the absolute minimum.")) - form_lay.addRow(self.title_second_res_label) - - # Control if to display the locations of where the minimum was found - self.sec_locations_cb = FCCheckBox(_("Other distances points coordinates")) - self.sec_locations_cb.setToolTip(_("Other distances and the coordinates for points\n" - "where the distance was found.")) - form_lay.addRow(self.sec_locations_cb) - - # this way I can hide/show the frame - self.sec_locations_frame = QtWidgets.QFrame() - self.sec_locations_frame.setContentsMargins(0, 0, 0, 0) - self.layout.addWidget(self.sec_locations_frame) - self.distances_box = QtWidgets.QVBoxLayout() - self.distances_box.setContentsMargins(0, 0, 0, 0) - self.sec_locations_frame.setLayout(self.distances_box) - - # Other Distances label - self.distances_label = QtWidgets.QLabel('%s' % _("Gerber distances")) - self.distances_label.setToolTip(_("Other distances and the coordinates for points\n" - "where the distance was found.")) - self.distances_box.addWidget(self.distances_label) - - # Other distances - self.distances_textb = FCTextArea(parent=self) - self.distances_textb.setPlaceholderText( - _("Other distances and the coordinates for points\n" - "where the distance was found.") - ) - self.distances_textb.setReadOnly(True) - stylesheet = """ - QTextEdit { selection-background-color:blue; - selection-color:white; - } - """ - - self.distances_textb.setStyleSheet(stylesheet) - self.distances_box.addWidget(self.distances_textb) - - self.distances_box.addWidget(QtWidgets.QLabel('')) - - # Other Locations label - self.locations_label = QtWidgets.QLabel('%s' % _("Points coordinates")) - self.locations_label.setToolTip(_("Other distances and the coordinates for points\n" - "where the distance was found.")) - self.distances_box.addWidget(self.locations_label) - - # Locations where minimum was found - self.locations_sec_textb = FCTextArea(parent=self) - self.locations_sec_textb.setPlaceholderText( - _("Other distances and the coordinates for points\n" - "where the distance was found.") - ) - self.locations_sec_textb.setReadOnly(True) - stylesheet = """ - QTextEdit { selection-background-color:blue; - selection-color:white; - } - """ - - self.locations_sec_textb.setStyleSheet(stylesheet) - self.distances_box.addWidget(self.locations_sec_textb) - - # Jump button - self.locate_sec_button = QtWidgets.QPushButton(_("Jump to selected position")) - self.locate_sec_button.setToolTip( - _("Select a position in the Locations text box and then\n" - "click this button.") - ) - self.locate_sec_button.setMinimumWidth(60) - self.locate_sec_button.setDisabled(True) - self.distances_box.addWidget(self.locate_sec_button) - - # GO button - self.calculate_button = QtWidgets.QPushButton(_("Find Minimum")) - self.calculate_button.setToolTip( - _("Calculate the minimum distance between copper features,\n" - "this will allow the determination of the right tool to\n" - "use for isolation or copper clearing.") - ) - self.calculate_button.setStyleSheet(""" - QPushButton - { - font-weight: bold; - } - """) - self.calculate_button.setMinimumWidth(60) - self.layout.addWidget(self.calculate_button) - - self.layout.addStretch() - - # ## Reset Tool - self.reset_button = QtWidgets.QPushButton(_("Reset Tool")) - self.reset_button.setIcon(QtGui.QIcon(self.app.resource_location + '/reset32.png')) - self.reset_button.setToolTip( - _("Will reset the tool parameters.") - ) - self.reset_button.setStyleSheet(""" - QPushButton - { - font-weight: bold; - } - """) - self.layout.addWidget(self.reset_button) - - self.loc_ois = OptionalHideInputSection(self.locations_cb, [self.locations_textb, self.locate_button]) - self.sec_loc_ois = OptionalHideInputSection(self.sec_locations_cb, [self.sec_locations_frame]) - # ################## Finished GUI creation ################################### - # ############################################################################ + # ############################################################################# + # ######################### Tool GUI ########################################## + # ############################################################################# + self.ui = OptimalUI(layout=self.layout, app=self.app) + self.toolName = self.ui.toolName # this is the line selected in the textbox with the locations of the minimum self.selected_text = '' @@ -271,17 +58,17 @@ class ToolOptimal(AppTool): # ############################################################################ # ############################ Signals ####################################### # ############################################################################ - self.calculate_button.clicked.connect(self.find_minimum_distance) - self.locate_button.clicked.connect(self.on_locate_position) - self.update_text.connect(self.on_update_text) - self.locations_textb.cursorPositionChanged.connect(self.on_textbox_clicked) + self.ui.calculate_button.clicked.connect(self.find_minimum_distance) + self.ui.locate_button.clicked.connect(self.on_locate_position) + self.ui.update_text.connect(self.on_update_text) + self.ui.locations_textb.cursorPositionChanged.connect(self.on_textbox_clicked) - self.locate_sec_button.clicked.connect(self.on_locate_sec_position) - self.update_sec_distances.connect(self.on_update_sec_distances_txt) - self.distances_textb.cursorPositionChanged.connect(self.on_distances_textb_clicked) - self.locations_sec_textb.cursorPositionChanged.connect(self.on_locations_sec_clicked) + self.ui.locate_sec_button.clicked.connect(self.on_locate_sec_position) + self.ui.update_sec_distances.connect(self.on_update_sec_distances_txt) + self.ui.distances_textb.cursorPositionChanged.connect(self.on_distances_textb_clicked) + self.ui.locations_sec_textb.cursorPositionChanged.connect(self.on_locations_sec_clicked) - self.reset_button.clicked.connect(self.set_tool_ui) + self.ui.reset_button.clicked.connect(self.set_tool_ui) def install(self, icon=None, separator=None, **kwargs): AppTool.install(self, icon, separator, shortcut='Alt+O', **kwargs) @@ -314,13 +101,13 @@ class ToolOptimal(AppTool): self.app.ui.notebook.setTabText(2, _("Optimal Tool")) def set_tool_ui(self): - self.result_entry.set_value(0.0) - self.freq_entry.set_value('0') + self.ui.result_entry.set_value(0.0) + self.ui.freq_entry.set_value('0') - self.precision_spinner.set_value(int(self.app.defaults["tools_opt_precision"])) - self.locations_textb.clear() + self.ui.precision_spinner.set_value(int(self.app.defaults["tools_opt_precision"])) + self.ui.locations_textb.clear() # new cursor - select all document - cursor = self.locations_textb.textCursor() + cursor = self.ui.locations_textb.textCursor() cursor.select(QtGui.QTextCursor.Document) # clear previous selection highlight @@ -328,20 +115,20 @@ class ToolOptimal(AppTool): tmp.clearBackground() cursor.setBlockFormat(tmp) - self.locations_textb.setVisible(False) - self.locate_button.setVisible(False) + self.ui.locations_textb.setVisible(False) + self.ui.locate_button.setVisible(False) - self.result_entry.set_value(0.0) - self.freq_entry.set_value('0') + self.ui.result_entry.set_value(0.0) + self.ui.freq_entry.set_value('0') self.reset_fields() def find_minimum_distance(self): self.units = self.app.defaults['units'].upper() - self.decimals = int(self.precision_spinner.get_value()) + self.decimals = int(self.ui.precision_spinner.get_value()) - selection_index = self.gerber_object_combo.currentIndex() + selection_index = self.ui.gerber_object_combo.currentIndex() - model_index = self.app.collection.index(selection_index, 0, self.gerber_object_combo.rootModelIndex()) + model_index = self.app.collection.index(selection_index, 0, self.ui.gerber_object_combo.rootModelIndex()) try: fcobj = model_index.internalPointer().obj except Exception as e: @@ -426,17 +213,16 @@ class ToolOptimal(AppTool): old_disp_number = disp_number idx += 1 - app_obj.inform.emit( - _("Optimal Tool. Finding the minimum distance.")) + app_obj.inform.emit(_("Optimal Tool. Finding the minimum distance.")) min_list = list(self.min_dict.keys()) min_dist = min(min_list) min_dist_string = '%.*f' % (self.decimals, float(min_dist)) - self.result_entry.set_value(min_dist_string) + self.ui.result_entry.set_value(min_dist_string) freq = len(self.min_dict[min_dist]) freq = '%d' % int(freq) - self.freq_entry.set_value(freq) + self.ui.freq_entry.set_value(freq) min_locations = self.min_dict.pop(min_dist) @@ -484,12 +270,12 @@ class ToolOptimal(AppTool): for loc in data: if loc: txt += '%s, %s\n' % (str(loc[0]), str(loc[1])) - self.locations_textb.setPlainText(txt) - self.locate_button.setDisabled(False) + self.ui.locations_textb.setPlainText(txt) + self.ui.locate_button.setDisabled(False) def on_textbox_clicked(self): # new cursor - select all document - cursor = self.locations_textb.textCursor() + cursor = self.ui.locations_textb.textCursor() cursor.select(QtGui.QTextCursor.Document) # clear previous selection highlight @@ -498,7 +284,7 @@ class ToolOptimal(AppTool): cursor.setBlockFormat(tmp) # new cursor - select the current line - cursor = self.locations_textb.textCursor() + cursor = self.ui.locations_textb.textCursor() cursor.select(QtGui.QTextCursor.LineUnderCursor) # highlight the current selected line @@ -513,12 +299,12 @@ class ToolOptimal(AppTool): txt = '' for loc in distance_list: txt += '%s\n' % str(loc) - self.distances_textb.setPlainText(txt) - self.locate_sec_button.setDisabled(False) + self.ui.distances_textb.setPlainText(txt) + self.ui.locate_sec_button.setDisabled(False) def on_distances_textb_clicked(self): # new cursor - select all document - cursor = self.distances_textb.textCursor() + cursor = self.ui.distances_textb.textCursor() cursor.select(QtGui.QTextCursor.Document) # clear previous selection highlight @@ -527,7 +313,7 @@ class ToolOptimal(AppTool): cursor.setBlockFormat(tmp) # new cursor - select the current line - cursor = self.distances_textb.textCursor() + cursor = self.ui.distances_textb.textCursor() cursor.select(QtGui.QTextCursor.LineUnderCursor) # highlight the current selected line @@ -545,11 +331,11 @@ class ToolOptimal(AppTool): for loc in distance_list: if loc: txt += '%s, %s\n' % (str(loc[0]), str(loc[1])) - self.locations_sec_textb.setPlainText(txt) + self.ui.locations_sec_textb.setPlainText(txt) def on_locations_sec_clicked(self): # new cursor - select all document - cursor = self.locations_sec_textb.textCursor() + cursor = self.ui.locations_sec_textb.textCursor() cursor.select(QtGui.QTextCursor.Document) # clear previous selection highlight @@ -558,7 +344,7 @@ class ToolOptimal(AppTool): cursor.setBlockFormat(tmp) # new cursor - select the current line - cursor = self.locations_sec_textb.textCursor() + cursor = self.ui.locations_sec_textb.textCursor() cursor.select(QtGui.QTextCursor.LineUnderCursor) # highlight the current selected line @@ -593,5 +379,247 @@ class ToolOptimal(AppTool): return def reset_fields(self): + self.ui.gerber_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) + self.ui.gerber_object_combo.setCurrentIndex(0) + + +class OptimalUI: + + toolName = _("Optimal Tool") + + def __init__(self, layout, app): + self.app = app + self.decimals = self.app.decimals + self.layout = layout + self.units = self.app.defaults['units'].upper() + + # ## Title + title_label = QtWidgets.QLabel("%s" % self.toolName) + title_label.setStyleSheet(""" + QLabel + { + font-size: 16px; + font-weight: bold; + } + """) + self.layout.addWidget(title_label) + self.layout.addWidget(QtWidgets.QLabel("")) + + # ## Form Layout + form_lay = QtWidgets.QFormLayout() + self.layout.addLayout(form_lay) + + # ## Gerber Object to mirror + self.gerber_object_combo = FCComboBox() + self.gerber_object_combo.setModel(self.app.collection) self.gerber_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) - self.gerber_object_combo.setCurrentIndex(0) + self.gerber_object_combo.is_last = True + self.gerber_object_combo.obj_type = "Gerber" + + self.gerber_object_label = QtWidgets.QLabel("%s:" % _("GERBER")) + self.gerber_object_label.setToolTip( + "Gerber object for which to find the minimum distance between copper features." + ) + form_lay.addRow(self.gerber_object_label) + form_lay.addRow(self.gerber_object_combo) + + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + form_lay.addRow(separator_line) + + # Precision = nr of decimals + self.precision_label = QtWidgets.QLabel('%s:' % _("Precision")) + self.precision_label.setToolTip(_("Number of decimals kept for found distances.")) + + self.precision_spinner = FCSpinner(callback=self.confirmation_message_int) + self.precision_spinner.set_range(2, 10) + self.precision_spinner.setWrapping(True) + form_lay.addRow(self.precision_label, self.precision_spinner) + + # Results Title + self.title_res_label = QtWidgets.QLabel('%s:' % _("Minimum distance")) + self.title_res_label.setToolTip(_("Display minimum distance between copper features.")) + form_lay.addRow(self.title_res_label) + + # Result value + self.result_label = QtWidgets.QLabel('%s:' % _("Determined")) + self.result_entry = FCEntry() + self.result_entry.setReadOnly(True) + + self.units_lbl = QtWidgets.QLabel(self.units.lower()) + self.units_lbl.setDisabled(True) + + hlay = QtWidgets.QHBoxLayout() + hlay.addWidget(self.result_entry) + hlay.addWidget(self.units_lbl) + + form_lay.addRow(self.result_label, hlay) + + # Frequency of minimum encounter + self.freq_label = QtWidgets.QLabel('%s:' % _("Occurring")) + self.freq_label.setToolTip(_("How many times this minimum is found.")) + self.freq_entry = FCEntry() + self.freq_entry.setReadOnly(True) + form_lay.addRow(self.freq_label, self.freq_entry) + + # Control if to display the locations of where the minimum was found + self.locations_cb = FCCheckBox(_("Minimum points coordinates")) + self.locations_cb.setToolTip(_("Coordinates for points where minimum distance was found.")) + form_lay.addRow(self.locations_cb) + + # Locations where minimum was found + self.locations_textb = FCTextArea(parent=self) + self.locations_textb.setPlaceholderText( + _("Coordinates for points where minimum distance was found.") + ) + self.locations_textb.setReadOnly(True) + stylesheet = """ + QTextEdit { selection-background-color:blue; + selection-color:white; + } + """ + + self.locations_textb.setStyleSheet(stylesheet) + form_lay.addRow(self.locations_textb) + + # Jump button + self.locate_button = QtWidgets.QPushButton(_("Jump to selected position")) + self.locate_button.setToolTip( + _("Select a position in the Locations text box and then\n" + "click this button.") + ) + self.locate_button.setMinimumWidth(60) + self.locate_button.setDisabled(True) + form_lay.addRow(self.locate_button) + + # Other distances in Gerber + self.title_second_res_label = QtWidgets.QLabel('%s:' % _("Other distances")) + self.title_second_res_label.setToolTip(_("Will display other distances in the Gerber file ordered from\n" + "the minimum to the maximum, not including the absolute minimum.")) + form_lay.addRow(self.title_second_res_label) + + # Control if to display the locations of where the minimum was found + self.sec_locations_cb = FCCheckBox(_("Other distances points coordinates")) + self.sec_locations_cb.setToolTip(_("Other distances and the coordinates for points\n" + "where the distance was found.")) + form_lay.addRow(self.sec_locations_cb) + + # this way I can hide/show the frame + self.sec_locations_frame = QtWidgets.QFrame() + self.sec_locations_frame.setContentsMargins(0, 0, 0, 0) + self.layout.addWidget(self.sec_locations_frame) + self.distances_box = QtWidgets.QVBoxLayout() + self.distances_box.setContentsMargins(0, 0, 0, 0) + self.sec_locations_frame.setLayout(self.distances_box) + + # Other Distances label + self.distances_label = QtWidgets.QLabel('%s' % _("Gerber distances")) + self.distances_label.setToolTip(_("Other distances and the coordinates for points\n" + "where the distance was found.")) + self.distances_box.addWidget(self.distances_label) + + # Other distances + self.distances_textb = FCTextArea(parent=self) + self.distances_textb.setPlaceholderText( + _("Other distances and the coordinates for points\n" + "where the distance was found.") + ) + self.distances_textb.setReadOnly(True) + stylesheet = """ + QTextEdit { selection-background-color:blue; + selection-color:white; + } + """ + + self.distances_textb.setStyleSheet(stylesheet) + self.distances_box.addWidget(self.distances_textb) + + self.distances_box.addWidget(QtWidgets.QLabel('')) + + # Other Locations label + self.locations_label = QtWidgets.QLabel('%s' % _("Points coordinates")) + self.locations_label.setToolTip(_("Other distances and the coordinates for points\n" + "where the distance was found.")) + self.distances_box.addWidget(self.locations_label) + + # Locations where minimum was found + self.locations_sec_textb = FCTextArea(parent=self) + self.locations_sec_textb.setPlaceholderText( + _("Other distances and the coordinates for points\n" + "where the distance was found.") + ) + self.locations_sec_textb.setReadOnly(True) + stylesheet = """ + QTextEdit { selection-background-color:blue; + selection-color:white; + } + """ + + self.locations_sec_textb.setStyleSheet(stylesheet) + self.distances_box.addWidget(self.locations_sec_textb) + + # Jump button + self.locate_sec_button = QtWidgets.QPushButton(_("Jump to selected position")) + self.locate_sec_button.setToolTip( + _("Select a position in the Locations text box and then\n" + "click this button.") + ) + self.locate_sec_button.setMinimumWidth(60) + self.locate_sec_button.setDisabled(True) + self.distances_box.addWidget(self.locate_sec_button) + + # GO button + self.calculate_button = QtWidgets.QPushButton(_("Find Minimum")) + self.calculate_button.setToolTip( + _("Calculate the minimum distance between copper features,\n" + "this will allow the determination of the right tool to\n" + "use for isolation or copper clearing.") + ) + self.calculate_button.setStyleSheet(""" + QPushButton + { + font-weight: bold; + } + """) + self.calculate_button.setMinimumWidth(60) + self.layout.addWidget(self.calculate_button) + + self.layout.addStretch() + + # ## Reset Tool + self.reset_button = QtWidgets.QPushButton(_("Reset Tool")) + self.reset_button.setIcon(QtGui.QIcon(self.app.resource_location + '/reset32.png')) + self.reset_button.setToolTip( + _("Will reset the tool parameters.") + ) + self.reset_button.setStyleSheet(""" + QPushButton + { + font-weight: bold; + } + """) + self.layout.addWidget(self.reset_button) + + self.loc_ois = OptionalHideInputSection(self.locations_cb, [self.locations_textb, self.locate_button]) + self.sec_loc_ois = OptionalHideInputSection(self.sec_locations_cb, [self.sec_locations_frame]) + + # #################################### FINSIHED GUI ########################### + # ############################################################################# + + def confirmation_message(self, accepted, minval, maxval): + if accepted is False: + self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' % (_("Edited value is out of range"), + self.decimals, + minval, + self.decimals, + maxval), False) + else: + self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False) + + def confirmation_message_int(self, accepted, minval, maxval): + if accepted is False: + self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' % + (_("Edited value is out of range"), minval, maxval), False) + else: + self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False) diff --git a/appTools/ToolPcbWizard.py b/appTools/ToolPcbWizard.py index 0cb93bd8..5d133a2f 100644 --- a/appTools/ToolPcbWizard.py +++ b/appTools/ToolPcbWizard.py @@ -8,7 +8,7 @@ from PyQt5 import QtWidgets, QtCore from appTool import AppTool -from appGUI.GUIElements import RadioSet, FCSpinner, FCButton, FCTable +from appGUI.GUIElements import RadioSet, FCSpinner, FCButton, FCTable, FCLabel import re import os @@ -28,121 +28,17 @@ class PcbWizard(AppTool): file_loaded = QtCore.pyqtSignal(str, str) - toolName = _("PcbWizard Import Tool") - def __init__(self, app): AppTool.__init__(self, app) self.app = app self.decimals = self.app.decimals - # Title - title_label = QtWidgets.QLabel("%s" % _('Import 2-file Excellon')) - title_label.setStyleSheet(""" - QLabel - { - font-size: 16px; - font-weight: bold; - } - """) - self.layout.addWidget(title_label) - - self.layout.addWidget(QtWidgets.QLabel("")) - self.layout.addWidget(QtWidgets.QLabel("%s:" % _("Load files"))) - - # Form Layout - form_layout = QtWidgets.QFormLayout() - self.layout.addLayout(form_layout) - - self.excellon_label = QtWidgets.QLabel('%s:' % _("Excellon file")) - self.excellon_label.setToolTip( - _("Load the Excellon file.\n" - "Usually it has a .DRL extension") - ) - self.excellon_brn = FCButton(_("Open")) - form_layout.addRow(self.excellon_label, self.excellon_brn) - - self.inf_label = QtWidgets.QLabel('%s:' % _("INF file")) - self.inf_label.setToolTip( - _("Load the INF file.") - ) - self.inf_btn = FCButton(_("Open")) - form_layout.addRow(self.inf_label, self.inf_btn) - - self.tools_table = FCTable() - self.layout.addWidget(self.tools_table) - - self.tools_table.setColumnCount(2) - self.tools_table.setHorizontalHeaderLabels(['#Tool', _('Diameter')]) - - self.tools_table.horizontalHeaderItem(0).setToolTip( - _("Tool Number")) - self.tools_table.horizontalHeaderItem(1).setToolTip( - _("Tool diameter in file units.")) - - # start with apertures table hidden - self.tools_table.setVisible(False) - - self.layout.addWidget(QtWidgets.QLabel("")) - self.layout.addWidget(QtWidgets.QLabel("%s:" % _("Excellon format"))) - # Form Layout - form_layout1 = QtWidgets.QFormLayout() - self.layout.addLayout(form_layout1) - - # Integral part of the coordinates - self.int_entry = FCSpinner(callback=self.confirmation_message_int) - self.int_entry.set_range(1, 10) - self.int_label = QtWidgets.QLabel('%s:' % _("Int. digits")) - self.int_label.setToolTip( - _("The number of digits for the integral part of the coordinates.") - ) - form_layout1.addRow(self.int_label, self.int_entry) - - # Fractional part of the coordinates - self.frac_entry = FCSpinner(callback=self.confirmation_message_int) - self.frac_entry.set_range(1, 10) - self.frac_label = QtWidgets.QLabel('%s:' % _("Frac. digits")) - self.frac_label.setToolTip( - _("The number of digits for the fractional part of the coordinates.") - ) - form_layout1.addRow(self.frac_label, self.frac_entry) - - # Zeros suppression for coordinates - self.zeros_radio = RadioSet([{'label': _('LZ'), 'value': 'LZ'}, - {'label': _('TZ'), 'value': 'TZ'}, - {'label': _('No Suppression'), 'value': 'D'}]) - self.zeros_label = QtWidgets.QLabel('%s:' % _("Zeros supp.")) - self.zeros_label.setToolTip( - _("The type of zeros suppression used.\n" - "Can be of type:\n" - "- LZ = leading zeros are kept\n" - "- TZ = trailing zeros are kept\n" - "- No Suppression = no zero suppression") - ) - form_layout1.addRow(self.zeros_label, self.zeros_radio) - - # Units type - self.units_radio = RadioSet([{'label': _('INCH'), 'value': 'INCH'}, - {'label': _('MM'), 'value': 'METRIC'}]) - self.units_label = QtWidgets.QLabel("%s:" % _('Units')) - self.units_label.setToolTip( - _("The type of units that the coordinates and tool\n" - "diameters are using. Can be INCH or MM.") - ) - form_layout1.addRow(self.units_label, self.units_radio) - - # Buttons - - self.import_button = QtWidgets.QPushButton(_("Import Excellon")) - self.import_button.setToolTip( - _("Import in FlatCAM an Excellon file\n" - "that store it's information's in 2 files.\n" - "One usually has .DRL extension while\n" - "the other has .INF extension.") - ) - self.layout.addWidget(self.import_button) - - self.layout.addStretch() + # ############################################################################# + # ######################### Tool GUI ########################################## + # ############################################################################# + self.ui = WizardUI(layout=self.layout, app=self.app) + self.toolName = self.ui.toolName self.excellon_loaded = False self.inf_loaded = False @@ -151,13 +47,13 @@ class PcbWizard(AppTool): self.modified_excellon_file = '' # ## Signals - self.excellon_brn.clicked.connect(self.on_load_excellon_click) - self.inf_btn.clicked.connect(self.on_load_inf_click) - self.import_button.clicked.connect(lambda: self.on_import_excellon( + self.ui.excellon_brn.clicked.connect(self.on_load_excellon_click) + self.ui.inf_btn.clicked.connect(self.on_load_inf_click) + self.ui.import_button.clicked.connect(lambda: self.on_import_excellon( excellon_fileobj=self.modified_excellon_file)) self.file_loaded.connect(self.on_file_loaded) - self.units_radio.activated_custom.connect(self.on_units_change) + self.ui.units_radio.activated_custom.connect(self.ui.on_units_change) self.units = 'INCH' self.zeros = 'LZ' @@ -211,10 +107,10 @@ class PcbWizard(AppTool): self.tools_from_inf = {} # ## Initialize form - self.int_entry.set_value(self.integral) - self.frac_entry.set_value(self.fractional) - self.zeros_radio.set_value(self.zeros) - self.units_radio.set_value(self.units) + self.ui.int_entry.set_value(self.integral) + self.ui.frac_entry.set_value(self.fractional) + self.ui.zeros_radio.set_value(self.zeros) + self.ui.units_radio.set_value(self.units) self.excellon_loaded = False self.inf_loaded = False @@ -227,57 +123,49 @@ class PcbWizard(AppTool): sorted_tools = [] if not self.tools_from_inf: - self.tools_table.setVisible(False) + self.ui.tools_table.setVisible(False) else: sort = [] for k, v in list(self.tools_from_inf.items()): sort.append(int(k)) sorted_tools = sorted(sort) n = len(sorted_tools) - self.tools_table.setRowCount(n) + self.ui.tools_table.setRowCount(n) tool_row = 0 for tool in sorted_tools: tool_id_item = QtWidgets.QTableWidgetItem('%d' % int(tool)) tool_id_item.setFlags(QtCore.Qt.ItemIsEnabled) - self.tools_table.setItem(tool_row, 0, tool_id_item) # Tool name/id + self.ui.tools_table.setItem(tool_row, 0, tool_id_item) # Tool name/id tool_dia_item = QtWidgets.QTableWidgetItem(str(self.tools_from_inf[tool])) tool_dia_item.setFlags(QtCore.Qt.ItemIsEnabled) - self.tools_table.setItem(tool_row, 1, tool_dia_item) + self.ui.tools_table.setItem(tool_row, 1, tool_dia_item) tool_row += 1 - self.tools_table.resizeColumnsToContents() - self.tools_table.resizeRowsToContents() + self.ui.tools_table.resizeColumnsToContents() + self.ui.tools_table.resizeRowsToContents() - vertical_header = self.tools_table.verticalHeader() + vertical_header = self.ui.tools_table.verticalHeader() vertical_header.hide() - self.tools_table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.ui.tools_table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) - horizontal_header = self.tools_table.horizontalHeader() + horizontal_header = self.ui.tools_table.horizontalHeader() # horizontal_header.setMinimumSectionSize(10) # horizontal_header.setDefaultSectionSize(70) horizontal_header.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents) horizontal_header.setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch) - self.tools_table.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) - self.tools_table.setSortingEnabled(False) - self.tools_table.setMinimumHeight(self.tools_table.getHeight()) - self.tools_table.setMaximumHeight(self.tools_table.getHeight()) + self.ui.tools_table.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.ui.tools_table.setSortingEnabled(False) + self.ui.tools_table.setMinimumHeight(self.ui.tools_table.getHeight()) + self.ui.tools_table.setMaximumHeight(self.ui.tools_table.getHeight()) def update_params(self): - self.units = self.units_radio.get_value() - self.zeros = self.zeros_radio.get_value() - self.integral = self.int_entry.get_value() - self.fractional = self.frac_entry.get_value() - - def on_units_change(self, val): - if val == 'INCH': - self.int_entry.set_value(2) - self.frac_entry.set_value(4) - else: - self.int_entry.set_value(3) - self.frac_entry.set_value(3) + self.units = self.ui.units_radio.get_value() + self.zeros = self.ui.zeros_radio.get_value() + self.integral = self.ui.int_entry.get_value() + self.fractional = self.ui.frac_entry.get_value() def on_load_excellon_click(self): """ @@ -357,9 +245,9 @@ class PcbWizard(AppTool): self.units = 'INCH' else: self.units = 'METRIC' - self.units_radio.set_value(self.units) - self.int_entry.set_value(self.integral) - self.frac_entry.set_value(self.fractional) + self.ui.units_radio.set_value(self.units) + self.ui.int_entry.set_value(self.integral) + self.ui.frac_entry.set_value(self.fractional) if not self.tools_from_inf: self.app.inform.emit('[ERROR] %s' % @@ -382,14 +270,12 @@ class PcbWizard(AppTool): if signal == 'inf': self.inf_loaded = True - self.tools_table.setVisible(True) - self.app.inform.emit('[success] %s' % - _("PcbWizard .INF file loaded.")) + self.ui.tools_table.setVisible(True) + self.app.inform.emit('[success] %s' % _("PcbWizard .INF file loaded.")) elif signal == 'excellon': self.excellon_loaded = True self.outname = os.path.split(str(filename))[1] - self.app.inform.emit('[success] %s' % - _("Main PcbWizard Excellon file loaded.")) + self.app.inform.emit('[success] %s' % _("Main PcbWizard Excellon file loaded.")) if self.excellon_loaded and self.inf_loaded: self.update_params() @@ -467,3 +353,149 @@ class PcbWizard(AppTool): self.app.inform.emit('[WARNING_NOTCL] %s' % _('Excellon merging is in progress. Please wait...')) else: self.app.inform.emit('[ERROR_NOTCL] %s' % _('The imported Excellon file is empty.')) + + +class WizardUI: + + toolName = _("PcbWizard Import Tool") + + def __init__(self, layout, app): + self.app = app + self.decimals = self.app.decimals + self.layout = layout + + # ## Title + title_label = FCLabel("%s" % self.toolName) + title_label.setStyleSheet(""" + QLabel + { + font-size: 16px; + font-weight: bold; + } + """) + self.layout.addWidget(title_label) + self.layout.addWidget(FCLabel("")) + + self.layout.addWidget(FCLabel("%s:" % _("Load files"))) + + # Form Layout + form_layout = QtWidgets.QFormLayout() + self.layout.addLayout(form_layout) + + self.excellon_label = FCLabel('%s:' % _("Excellon file")) + self.excellon_label.setToolTip( + _("Load the Excellon file.\n" + "Usually it has a .DRL extension") + ) + self.excellon_brn = FCButton(_("Open")) + form_layout.addRow(self.excellon_label, self.excellon_brn) + + self.inf_label = FCLabel('%s:' % _("INF file")) + self.inf_label.setToolTip( + _("Load the INF file.") + ) + self.inf_btn = FCButton(_("Open")) + form_layout.addRow(self.inf_label, self.inf_btn) + + self.tools_table = FCTable() + self.layout.addWidget(self.tools_table) + + self.tools_table.setColumnCount(2) + self.tools_table.setHorizontalHeaderLabels(['#Tool', _('Diameter')]) + + self.tools_table.horizontalHeaderItem(0).setToolTip( + _("Tool Number")) + self.tools_table.horizontalHeaderItem(1).setToolTip( + _("Tool diameter in file units.")) + + # start with apertures table hidden + self.tools_table.setVisible(False) + + self.layout.addWidget(FCLabel("")) + self.layout.addWidget(FCLabel("%s:" % _("Excellon format"))) + # Form Layout + form_layout1 = QtWidgets.QFormLayout() + self.layout.addLayout(form_layout1) + + # Integral part of the coordinates + self.int_entry = FCSpinner(callback=self.confirmation_message_int) + self.int_entry.set_range(1, 10) + self.int_label = FCLabel('%s:' % _("Int. digits")) + self.int_label.setToolTip( + _("The number of digits for the integral part of the coordinates.") + ) + form_layout1.addRow(self.int_label, self.int_entry) + + # Fractional part of the coordinates + self.frac_entry = FCSpinner(callback=self.confirmation_message_int) + self.frac_entry.set_range(1, 10) + self.frac_label = FCLabel('%s:' % _("Frac. digits")) + self.frac_label.setToolTip( + _("The number of digits for the fractional part of the coordinates.") + ) + form_layout1.addRow(self.frac_label, self.frac_entry) + + # Zeros suppression for coordinates + self.zeros_radio = RadioSet([{'label': _('LZ'), 'value': 'LZ'}, + {'label': _('TZ'), 'value': 'TZ'}, + {'label': _('No Suppression'), 'value': 'D'}]) + self.zeros_label = FCLabel('%s:' % _("Zeros supp.")) + self.zeros_label.setToolTip( + _("The type of zeros suppression used.\n" + "Can be of type:\n" + "- LZ = leading zeros are kept\n" + "- TZ = trailing zeros are kept\n" + "- No Suppression = no zero suppression") + ) + form_layout1.addRow(self.zeros_label, self.zeros_radio) + + # Units type + self.units_radio = RadioSet([{'label': _('INCH'), 'value': 'INCH'}, + {'label': _('MM'), 'value': 'METRIC'}]) + self.units_label = FCLabel("%s:" % _('Units')) + self.units_label.setToolTip( + _("The type of units that the coordinates and tool\n" + "diameters are using. Can be INCH or MM.") + ) + form_layout1.addRow(self.units_label, self.units_radio) + + # Buttons + + self.import_button = QtWidgets.QPushButton(_("Import Excellon")) + self.import_button.setToolTip( + _("Import in FlatCAM an Excellon file\n" + "that store it's information's in 2 files.\n" + "One usually has .DRL extension while\n" + "the other has .INF extension.") + ) + self.layout.addWidget(self.import_button) + + self.layout.addStretch() + + # #################################### FINSIHED GUI ########################### + # ############################################################################# + + def on_units_change(self, val): + if val == 'INCH': + self.int_entry.set_value(2) + self.frac_entry.set_value(4) + else: + self.int_entry.set_value(3) + self.frac_entry.set_value(3) + + def confirmation_message(self, accepted, minval, maxval): + if accepted is False: + self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' % (_("Edited value is out of range"), + self.decimals, + minval, + self.decimals, + maxval), False) + else: + self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False) + + def confirmation_message_int(self, accepted, minval, maxval): + if accepted is False: + self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' % + (_("Edited value is out of range"), minval, maxval), False) + else: + self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)