diff --git a/FlatCAMApp.py b/FlatCAMApp.py index bee19cc9..31c3a45e 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -2372,6 +2372,7 @@ class App(QtCore.QObject): self.film_tool = None self.paste_tool = None self.calculator_tool = None + self.rules_tool = None self.sub_tool = None self.move_tool = None self.cutout_tool = None @@ -2909,6 +2910,9 @@ class App(QtCore.QObject): self.sub_tool = ToolSub(self) self.sub_tool.install(icon=QtGui.QIcon('share/sub32.png'), pos=self.ui.menutool, separator=True) + self.rules_tool = RulesCheck(self) + self.rules_tool.install(icon=QtGui.QIcon('share/rules32.png'), pos=self.ui.menutool, separator=True) + self.move_tool = ToolMove(self) self.move_tool.install(icon=QtGui.QIcon('share/move16.png'), pos=self.ui.menuedit, before=self.ui.menueditorigin) @@ -3036,6 +3040,7 @@ class App(QtCore.QObject): self.ui.film_btn.triggered.connect(lambda: self.film_tool.run(toggle=True)) self.ui.solder_btn.triggered.connect(lambda: self.paste_tool.run(toggle=True)) self.ui.sub_btn.triggered.connect(lambda: self.sub_tool.run(toggle=True)) + self.ui.rules_btn.triggered.connect(lambda: self.rules_tool.run(toggle=True)) self.ui.calculators_btn.triggered.connect(lambda: self.calculator_tool.run(toggle=True)) self.ui.transform_btn.triggered.connect(lambda: self.transform_tool.run(toggle=True)) @@ -6979,9 +6984,22 @@ class App(QtCore.QObject): # return if not custom_location: + dia_box_location = None + + try: + dia_box_location = eval(self.clipboard.text()) + except Exception as e: + pass + + if type(dia_box_location) == tuple: + dia_box_location = str(dia_box_location) + else: + dia_box_location = None + dia_box = Dialog_box(title=_("Jump to ..."), label=_("Enter the coordinates in format X,Y:"), - icon=QtGui.QIcon('share/jump_to16.png')) + icon=QtGui.QIcon('share/jump_to16.png'), + initial_text=dia_box_location) if dia_box.ok is True: try: @@ -7004,7 +7022,8 @@ class App(QtCore.QObject): if self.is_legacy is False: canvas_origin = self.plotcanvas.native.mapToGlobal(QtCore.QPoint(0, 0)) jump_loc = self.plotcanvas.translate_coords_2((location[0], location[1])) - cursor.setPos(canvas_origin.x() + jump_loc[0], (canvas_origin.y() + jump_loc[1])) + j_pos = (canvas_origin.x() + jump_loc[0], (canvas_origin.y() + jump_loc[1])) + cursor.setPos(j_pos[0], j_pos[1]) else: # find the canvas origin which is in the top left corner canvas_origin = self.plotcanvas.native.mapToGlobal(QtCore.QPoint(0, 0)) @@ -7015,8 +7034,22 @@ class App(QtCore.QObject): # in pixels where the origin 0,0 is in the lowest left point of the display window (in our case is the # canvas) and the point (width, height) is in the top-right location loc = self.plotcanvas.axes.transData.transform_point(location) + j_pos = (x0 + loc[0], y0 - loc[1]) + cursor.setPos(j_pos[0], j_pos[1]) - cursor.setPos(x0 + loc[0], y0 - loc[1]) + if self.grid_status() == True: + # Update cursor + self.app_cursor.set_data(np.asarray([(location[0], location[1])]), + symbol='++', edge_color='black', size=self.defaults["global_cursor_size"]) + + # Set the position label + self.ui.position_label.setText("    X: %.4f   " + "Y: %.4f" % (location[0], location[1])) + # Set the relative position label + dx = location[0] - float(self.rel_point1[0]) + dy = location[1] - float(self.rel_point1[1]) + self.ui.rel_position_label.setText("Dx: %.4f   Dy: " + "%.4f    " % (dx, dy)) self.inform.emit('[success] %s' % _("Done.")) @@ -7799,8 +7832,6 @@ class App(QtCore.QObject): self.pos = (self.pos_canvas[0], self.pos_canvas[1]) try: - modifiers = QtWidgets.QApplication.keyboardModifiers() - if event.button == 1: # Reset here the relative coordinates so there is a new reference on the click position if self.rel_point1 is None: @@ -7809,16 +7840,6 @@ class App(QtCore.QObject): self.rel_point2 = copy(self.rel_point1) self.rel_point1 = self.pos - # If the SHIFT key is pressed when LMB is clicked then the coordinates are copied to clipboard - if modifiers == QtCore.Qt.ShiftModifier: - # do not auto open the Project Tab - self.click_noproject = True - - self.clipboard.setText(self.defaults["global_point_clipboard_format"] % (self.pos[0], self.pos[1])) - self.inform.emit('[success] %s' % - _("Coordinates copied to clipboard.")) - return - self.on_mouse_move_over_plot(event, origin_click=True) except Exception as e: App.log.debug("App.on_mouse_click_over_plot() --> Outside plot? --> %s" % str(e)) @@ -7970,6 +7991,17 @@ class App(QtCore.QObject): # selection and then select a type of selection ("enclosing" or "touching") try: if event.button == 1: # left click + modifiers = QtWidgets.QApplication.keyboardModifiers() + # If the SHIFT key is pressed when LMB is clicked then the coordinates are copied to clipboard + if modifiers == QtCore.Qt.ShiftModifier: + # do not auto open the Project Tab + self.click_noproject = True + + self.clipboard.setText(self.defaults["global_point_clipboard_format"] % (self.pos[0], self.pos[1])) + self.inform.emit('[success] %s' % + _("Coordinates copied to clipboard.")) + return + if self.doubleclick is True: self.doubleclick = False if self.collection.get_selected(): diff --git a/README.md b/README.md index a28150c2..0016799f 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,10 @@ CAD program, and create G-Code for Isolation routing. ================================================= +28.09.2019 + +- changed the icon for Open Script and reused it for the Check Rules Tool + 27.09.2019 - optimized the toggle axis command @@ -19,6 +23,11 @@ CAD program, and create G-Code for Isolation routing. - if an object is edited but the result is not saved, the app will reload the edited object UI and set the Selected tab as active - made the mouse cursor (big, small) change in real time for both graphic engines - started to work on a new FlatCAM tool: Rules Check +- created the GUI for the Rule Check Tool +- if there are (x, y) coordinates in the clipboard, when launching the "Jump to" function, those coordinates will be preloaded in the Dialog box. +- when the combo SHIFT + LMB is executed there is no longer a deselection of objects +- when the "Jump to" function is called, the mouse cursor (if active) will be moved to the new position and the screen position labels will be updated accordingly + 27.09.2019 diff --git a/flatcamGUI/FlatCAMGUI.py b/flatcamGUI/FlatCAMGUI.py index a1821e5b..9950479f 100644 --- a/flatcamGUI/FlatCAMGUI.py +++ b/flatcamGUI/FlatCAMGUI.py @@ -112,7 +112,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.menufile_scripting.setToolTipsVisible(True) self.menufilenewscript = QtWidgets.QAction(QtGui.QIcon('share/script_new16.png'), _('New Script ...'), self) - self.menufileopenscript = QtWidgets.QAction(QtGui.QIcon('share/script_open16.png'), _('Open Script ...'), self) + self.menufileopenscript = QtWidgets.QAction(QtGui.QIcon('share/open_script32.png'), _('Open Script ...'), self) self.menufilerunscript = QtWidgets.QAction(QtGui.QIcon('share/script16.png'), '%s\tSHIFT+S' % _('Run Script ...'), self) self.menufilerunscript.setToolTip( @@ -664,7 +664,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow): # ## Shell Toolbar ## self.shell_btn = self.toolbarshell.addAction(QtGui.QIcon('share/shell32.png'), _("&Command Line")) self.new_script_btn = self.toolbarshell.addAction(QtGui.QIcon('share/script_new24.png'), _('New Script ...')) - self.open_script_btn = self.toolbarshell.addAction(QtGui.QIcon('share/script_open18.png'), _('Open Script ...')) + self.open_script_btn = self.toolbarshell.addAction(QtGui.QIcon('share/open_script32.png'), _('Open Script ...')) self.run_script_btn = self.toolbarshell.addAction(QtGui.QIcon('share/script16.png'), _('Run Script ...')) # ## Tools Toolbar ## @@ -678,6 +678,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.film_btn = self.toolbartools.addAction(QtGui.QIcon('share/film16.png'), _("Film Tool")) self.solder_btn = self.toolbartools.addAction(QtGui.QIcon('share/solderpastebis32.png'), _("SolderPaste Tool")) self.sub_btn = self.toolbartools.addAction(QtGui.QIcon('share/sub32.png'), _("Substract Tool")) + self.rules_btn = self.toolbartools.addAction(QtGui.QIcon('share/rules32.png'), _("Rules Tool")) self.toolbartools.addSeparator() @@ -1219,6 +1220,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow): ALT+D  %s + + ALT+E +  %s + ALT+K  %s @@ -1326,9 +1331,11 @@ class FlatCAMGUI(QtWidgets.QMainWindow): _("Open Project"), _("Save Project As"), _("Toggle Plot Area"), _("Copy Obj_Name"), _("Toggle Code Editor"), _("Toggle the axis"), _("Open Preferences Window"), _("Rotate by 90 degree CCW"), _("Run a Script"), _("Toggle the workspace"), _("Skew on X axis"), - _("Skew on Y axis"), _("Calculators Tool"), _("2-Sided PCB Tool"), _("Solder Paste Dispensing Tool"), + _("Skew on Y axis"), _("Calculators Tool"), _("2-Sided PCB Tool"), _("Transformations Tool"), + _("Solder Paste Dispensing Tool"), _("Film PCB Tool"), _("Non-Copper Clearing Tool"), - _("Paint Area Tool"), _("PDF Import Tool"), _("Transformations Tool"), _("View File Source"), + _("Paint Area Tool"), _("PDF Import Tool"), _("Rules Check Tool"), + _("View File Source"), _("Cutout PCB Tool"), _("Enable all Plots"), _("Disable all Plots"), _("Disable Non-selected Plots"), _("Toggle Full Screen"), _("Abort current task (gracefully)"), _("Open Online Manual"), _("Open Online Tutorials"), _("Refresh Plots"), _("Delete Object"), _("Alternate: Delete Tool"), @@ -2102,7 +2109,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow): # ## Shell Toolbar # ## self.shell_btn = self.toolbarshell.addAction(QtGui.QIcon('share/shell32.png'), _("&Command Line")) self.new_script_btn = self.toolbarshell.addAction(QtGui.QIcon('share/script_new24.png'), _('New Script ...')) - self.open_script_btn = self.toolbarshell.addAction(QtGui.QIcon('share/script_open18.png'), _('Open Script ...')) + self.open_script_btn = self.toolbarshell.addAction(QtGui.QIcon('share/open_script32.png'), _('Open Script ...')) self.run_script_btn = self.toolbarshell.addAction(QtGui.QIcon('share/script16.png'), _('Run Script ...')) # ## Tools Toolbar # ## @@ -2406,6 +2413,11 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.app.dblsidedtool.run(toggle=True) return + # Transformation Tool + if key == QtCore.Qt.Key_E: + self.app.transform_tool.run(toggle=True) + return + # Solder Paste Dispensing Tool if key == QtCore.Qt.Key_K: self.app.paste_tool.run(toggle=True) @@ -2431,9 +2443,9 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.app.pdf_tool.run() return - # Transformation Tool + # Rules Tool if key == QtCore.Qt.Key_R: - self.app.transform_tool.run(toggle=True) + self.app.rules_tool.run(toggle=True) return # View Source Object Content diff --git a/flatcamGUI/GUIElements.py b/flatcamGUI/GUIElements.py index d0bfbbab..5508d4aa 100644 --- a/flatcamGUI/GUIElements.py +++ b/flatcamGUI/GUIElements.py @@ -1734,21 +1734,26 @@ class FCDoubleSpinner(QtWidgets.QDoubleSpinBox): class Dialog_box(QtWidgets.QWidget): - def __init__(self, title=None, label=None, icon=None): + def __init__(self, title=None, label=None, icon=None, initial_text=None): """ :param title: string with the window title :param label: string with the message inside the dialog box """ super(Dialog_box, self).__init__() - self.location = (0, 0) + if initial_text is None: + self.location = str((0, 0)) + else: + self.location = initial_text + self.ok = False - dialog_box = QtWidgets.QInputDialog() - dialog_box.setMinimumWidth(290) + self.dialog_box = QtWidgets.QInputDialog() + self.dialog_box.setMinimumWidth(290) self.setWindowIcon(icon) - self.location, self.ok = dialog_box.getText(self, title, label, text="0, 0") + self.location, self.ok = self.dialog_box.getText(self, title, label, + text=str(self.location).replace('(', '').replace(')', '')) self.readyToEdit = True def mousePressEvent(self, e, parent=None): diff --git a/flatcamTools/ToolRulesCheck.py b/flatcamTools/ToolRulesCheck.py index 1095e4db..f4158700 100644 --- a/flatcamTools/ToolRulesCheck.py +++ b/flatcamTools/ToolRulesCheck.py @@ -22,7 +22,7 @@ if '_' not in builtins.__dict__: class RulesCheck(FlatCAMTool): - toolName = _("Check Rules PCB") + toolName = _("Check Rules") def __init__(self, app): super(RulesCheck, self).__init__(self) @@ -39,206 +39,308 @@ class RulesCheck(FlatCAMTool): """) self.layout.addWidget(title_label) - # Form Layout - form_layout_0 = QtWidgets.QFormLayout() - self.layout.addLayout(form_layout_0) - - # Type of object to be panelized - self.type_obj_combo = QtWidgets.QComboBox() - self.type_obj_combo.addItem("Gerber") - self.type_obj_combo.addItem("Excellon") - self.type_obj_combo.addItem("Geometry") - - self.type_obj_combo.setItemIcon(0, QtGui.QIcon("share/flatcam_icon16.png")) - self.type_obj_combo.setItemIcon(1, QtGui.QIcon("share/drill16.png")) - self.type_obj_combo.setItemIcon(2, QtGui.QIcon("share/geometry16.png")) - - self.type_obj_combo_label = QtWidgets.QLabel('%s:' % _("Object Type")) - self.type_obj_combo_label.setToolTip( - _("Specify the type of object to be panelized\n" - "It can be of type: Gerber, Excellon or Geometry.\n" - "The selection here decide the type of objects that will be\n" - "in the Object combobox.") - ) - form_layout_0.addRow(self.type_obj_combo_label, self.type_obj_combo) - - # Object to be panelized - self.object_combo = QtWidgets.QComboBox() - self.object_combo.setModel(self.app.collection) - self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) - self.object_combo.setCurrentIndex(1) - - self.object_label = QtWidgets.QLabel('%s:' % _("Object")) - self.object_label.setToolTip( - _("Object to be panelized. This means that it will\n" - "be duplicated in an array of rows and columns.") - ) - form_layout_0.addRow(self.object_label, self.object_combo) - form_layout_0.addRow(QtWidgets.QLabel("")) - # Form Layout form_layout = QtWidgets.QFormLayout() self.layout.addLayout(form_layout) - # Type of box Panel object - self.reference_radio = RadioSet([{'label': _('Object'), 'value': 'object'}, - {'label': _('Bounding Box'), 'value': 'bbox'}]) - self.box_label = QtWidgets.QLabel("%s:" % _("Penelization Reference")) - self.box_label.setToolTip( - _("Choose the reference for panelization:\n" - "- Object = the bounding box of a different object\n" - "- Bounding Box = the bounding box of the object to be panelized\n" - "\n" - "The reference is useful when doing panelization for more than one\n" - "object. The spacings (really offsets) will be applied in reference\n" - "to this reference object therefore maintaining the panelized\n" - "objects in sync.") + self.gerber_title_lbl = QtWidgets.QLabel('%s:' % _("Gerber Files")) + self.gerber_title_lbl.setToolTip( + _("Gerber files for which to check rules.") ) - form_layout.addRow(self.box_label) - form_layout.addRow(self.reference_radio) - # Type of Box Object to be used as an envelope for panelization - self.type_box_combo = QtWidgets.QComboBox() - self.type_box_combo.addItem("Gerber") - self.type_box_combo.addItem("Excellon") - self.type_box_combo.addItem("Geometry") + # Copper object + self.copper_object = QtWidgets.QComboBox() + self.copper_object.setModel(self.app.collection) + self.copper_object.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) + self.copper_object.setCurrentIndex(1) - # we get rid of item1 ("Excellon") as it is not suitable for use as a "box" for panelizing - self.type_box_combo.view().setRowHidden(1, True) - self.type_box_combo.setItemIcon(0, QtGui.QIcon("share/flatcam_icon16.png")) - self.type_box_combo.setItemIcon(2, QtGui.QIcon("share/geometry16.png")) - - self.type_box_combo_label = QtWidgets.QLabel('%s:' % _("Box Type")) - self.type_box_combo_label.setToolTip( - _("Specify the type of object to be used as an container for\n" - "panelization. It can be: Gerber or Geometry type.\n" - "The selection here decide the type of objects that will be\n" - "in the Box Object combobox.") + self.copper_object_lbl = QtWidgets.QLabel('%s:' % _("Copper")) + self.copper_object_lbl.setToolTip( + _("Object to be panelized. This means that it will\n" + "be duplicated in an array of rows and columns.") ) - form_layout.addRow(self.type_box_combo_label, self.type_box_combo) - # Box - self.box_combo = QtWidgets.QComboBox() - self.box_combo.setModel(self.app.collection) - self.box_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) - self.box_combo.setCurrentIndex(1) + # SolderMask object + self.sm_object = QtWidgets.QComboBox() + self.sm_object.setModel(self.app.collection) + self.sm_object.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) + self.sm_object.setCurrentIndex(1) - self.box_combo_label = QtWidgets.QLabel('%s:' % _("Box Object")) - self.box_combo_label.setToolTip( - _("The actual object that is used a container for the\n " - "selected object that is to be panelized.") + self.sm_object_lbl = QtWidgets.QLabel('%s:' % _("SolderMask")) + self.sm_object_lbl.setToolTip( + _("Object to be panelized. This means that it will\n" + "be duplicated in an array of rows and columns.") ) - form_layout.addRow(self.box_combo_label, self.box_combo) + + # SilkScreen object + self.ss_object = QtWidgets.QComboBox() + self.ss_object.setModel(self.app.collection) + self.ss_object.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) + self.ss_object.setCurrentIndex(1) + + self.ss_object_lbl = QtWidgets.QLabel('%s:' % _("Silkscreen")) + self.ss_object_lbl.setToolTip( + _("Object to be panelized. This means that it will\n" + "be duplicated in an array of rows and columns.") + ) + + # Outline object + self.outline_object = QtWidgets.QComboBox() + self.outline_object.setModel(self.app.collection) + self.outline_object.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) + self.outline_object.setCurrentIndex(1) + + self.outline_object_lbl = QtWidgets.QLabel('%s:' % _("Outline")) + self.outline_object_lbl.setToolTip( + _("Object to be panelized. This means that it will\n" + "be duplicated in an array of rows and columns.") + ) + form_layout.addRow(self.gerber_title_lbl) + form_layout.addRow(self.copper_object_lbl, self.copper_object) + form_layout.addRow(self.sm_object_lbl, self.sm_object) + form_layout.addRow(self.ss_object_lbl, self.ss_object) + form_layout.addRow(self.outline_object_lbl, self.outline_object) form_layout.addRow(QtWidgets.QLabel("")) - panel_data_label = QtWidgets.QLabel("%s:" % _("Panel Data")) - panel_data_label.setToolTip( - _("This informations will shape the resulting panel.\n" - "The number of rows and columns will set how many\n" - "duplicates of the original geometry will be generated.\n" - "\n" - "The spacings will set the distance between any two\n" - "elements of the panel array.") + self.excellon_title_lbl = QtWidgets.QLabel('%s:' % _("Excellon Files")) + self.excellon_title_lbl.setToolTip( + _("Excellon files for which to check rules.") ) - form_layout.addRow(panel_data_label) - # Spacing Columns - self.spacing_columns = FCEntry() - self.spacing_columns_label = QtWidgets.QLabel('%s:' % _("Spacing cols")) - self.spacing_columns_label.setToolTip( - _("Spacing between columns of the desired panel.\n" - "In current units.") - ) - form_layout.addRow(self.spacing_columns_label, self.spacing_columns) + # Excellon 1 object + self.e1_object = QtWidgets.QComboBox() + self.e1_object.setModel(self.app.collection) + self.e1_object.setRootModelIndex(self.app.collection.index(1, 0, QtCore.QModelIndex())) + self.e1_object.setCurrentIndex(1) - # Spacing Rows - self.spacing_rows = FCEntry() - self.spacing_rows_label = QtWidgets.QLabel('%s:' % _("Spacing rows")) - self.spacing_rows_label.setToolTip( - _("Spacing between rows of the desired panel.\n" - "In current units.") + self.e1_object_lbl = QtWidgets.QLabel('%s:' % _("Excellon 1")) + self.e1_object_lbl.setToolTip( + _("Object to be panelized. This means that it will\n" + "be duplicated in an array of rows and columns.") ) - form_layout.addRow(self.spacing_rows_label, self.spacing_rows) - # Columns - self.columns = FCEntry() - self.columns_label = QtWidgets.QLabel('%s:' % _("Columns")) - self.columns_label.setToolTip( - _("Number of columns of the desired panel") - ) - form_layout.addRow(self.columns_label, self.columns) + # Excellon 2 object + self.e2_object = QtWidgets.QComboBox() + self.e2_object.setModel(self.app.collection) + self.e2_object.setRootModelIndex(self.app.collection.index(1, 0, QtCore.QModelIndex())) + self.e2_object.setCurrentIndex(1) - # Rows - self.rows = FCEntry() - self.rows_label = QtWidgets.QLabel('%s:' % _("Rows")) - self.rows_label.setToolTip( - _("Number of rows of the desired panel") + self.e2_object_lbl = QtWidgets.QLabel('%s:' % _("Excellon 2")) + self.e2_object_lbl.setToolTip( + _("Object to be panelized. This means that it will\n" + "be duplicated in an array of rows and columns.") ) - form_layout.addRow(self.rows_label, self.rows) + + form_layout.addRow(self.excellon_title_lbl) + form_layout.addRow(self.e1_object_lbl, self.e1_object) + form_layout.addRow(self.e2_object_lbl, self.e2_object) form_layout.addRow(QtWidgets.QLabel("")) - # Type of resulting Panel object - self.panel_type_radio = RadioSet([{'label': _('Gerber'), 'value': 'gerber'}, - {'label': _('Geo'), 'value': 'geometry'}]) - self.panel_type_label = QtWidgets.QLabel("%s:" % _("Panel Type")) - self.panel_type_label.setToolTip( - _("Choose the type of object for the panel object:\n" - "- Geometry\n" - "- Gerber") - ) - form_layout.addRow(self.panel_type_label) - form_layout.addRow(self.panel_type_radio) + # Form Layout + form_layout_1 = QtWidgets.QFormLayout() + self.layout.addLayout(form_layout_1) - # Constrains - self.constrain_cb = FCCheckBox('%s:' % _("Constrain panel within")) - self.constrain_cb.setToolTip( - _("Area define by DX and DY within to constrain the panel.\n" - "DX and DY values are in current units.\n" - "Regardless of how many columns and rows are desired,\n" - "the final panel will have as many columns and rows as\n" - "they fit completely within selected area.") + # Copper2copper clearance + self.clearance_copper2copper_cb = FCCheckBox('%s:' % _("Copper to copper clearance")) + self.clearance_copper2copper_cb.setToolTip( + _("This checks if the minimum clearance between copper\n" + "features is met.") ) - form_layout.addRow(self.constrain_cb) + form_layout_1.addRow(self.clearance_copper2copper_cb) - self.x_width_entry = FCEntry() - self.x_width_lbl = QtWidgets.QLabel('%s:' % _("Width (DX)")) - self.x_width_lbl.setToolTip( - _("The width (DX) within which the panel must fit.\n" - "In current units.") + # Copper2copper clearance value + self.clearance_copper2copper_entry = FCEntry() + self.clearance_copper2copper_lbl = QtWidgets.QLabel('%s:' % _("Min value")) + self.clearance_copper2copper_lbl.setToolTip( + _("Minimum acceptable clearance value.") ) - form_layout.addRow(self.x_width_lbl, self.x_width_entry) + form_layout_1.addRow(self.clearance_copper2copper_lbl, self.clearance_copper2copper_entry) - self.y_height_entry = FCEntry() - self.y_height_lbl = QtWidgets.QLabel('%s:' % _("Height (DY)")) - self.y_height_lbl.setToolTip( - _("The height (DY)within which the panel must fit.\n" - "In current units.") + self.c2c = OptionalInputSection( + self.clearance_copper2copper_cb, [self.clearance_copper2copper_lbl, self.clearance_copper2copper_entry]) + + # Copper2soldermask clearance + self.clearance_copper2sm_cb = FCCheckBox('%s:' % _("Copper to soldermask clearance")) + self.clearance_copper2sm_cb.setToolTip( + _("This checks if the minimum clearance between copper\n" + "features and soldermask features is met.") ) - form_layout.addRow(self.y_height_lbl, self.y_height_entry) + form_layout_1.addRow(self.clearance_copper2sm_cb) - self.constrain_sel = OptionalInputSection( - self.constrain_cb, [self.x_width_lbl, self.x_width_entry, self.y_height_lbl, self.y_height_entry]) + # Copper2soldermask clearance value + self.clearance_copper2sm_entry = FCEntry() + self.clearance_copper2sm_lbl = QtWidgets.QLabel('%s:' % _("Min value")) + self.clearance_copper2sm_lbl.setToolTip( + _("Minimum acceptable clearance value.") + ) + form_layout_1.addRow(self.clearance_copper2sm_lbl, self.clearance_copper2sm_entry) + + self.c2sm = OptionalInputSection( + self.clearance_copper2sm_cb, [self.clearance_copper2sm_lbl, self.clearance_copper2sm_entry]) + + # Copper2silkscreen clearance + self.clearance_copper2sk_cb = FCCheckBox('%s:' % _("Copper to silkscreen clearance")) + self.clearance_copper2sk_cb.setToolTip( + _("This checks if the minimum clearance between copper\n" + "features and silkscreen features is met.") + ) + form_layout_1.addRow(self.clearance_copper2sk_cb) + + # Copper2silkscreen clearance value + self.clearance_copper2sk_entry = FCEntry() + self.clearance_copper2sk_lbl = QtWidgets.QLabel('%s:' % _("Min value")) + self.clearance_copper2sk_lbl.setToolTip( + _("Minimum acceptable clearance value.") + ) + form_layout_1.addRow(self.clearance_copper2sk_lbl, self.clearance_copper2sk_entry) + + self.c2sk = OptionalInputSection( + self.clearance_copper2sk_cb, [self.clearance_copper2sk_lbl, self.clearance_copper2sk_entry]) + + # Copper2outline clearance + self.clearance_copper2ol_cb = FCCheckBox('%s:' % _("Copper to outline clearance")) + self.clearance_copper2ol_cb.setToolTip( + _("This checks if the minimum clearance between copper\n" + "features and the outline is met.") + ) + form_layout_1.addRow(self.clearance_copper2ol_cb) + + # Copper2outline clearance value + self.clearance_copper2ol_entry = FCEntry() + self.clearance_copper2ol_lbl = QtWidgets.QLabel('%s:' % _("Min value")) + self.clearance_copper2ol_lbl.setToolTip( + _("Minimum acceptable clearance value.") + ) + form_layout_1.addRow(self.clearance_copper2ol_lbl, self.clearance_copper2ol_entry) + + self.c2ol = OptionalInputSection( + self.clearance_copper2ol_cb, [self.clearance_copper2ol_lbl, self.clearance_copper2ol_entry]) + + # Silkscreen2silkscreen clearance + self.clearance_silk2silk_cb = FCCheckBox('%s:' % _("Silkscreen to silkscreen clearance")) + self.clearance_silk2silk_cb.setToolTip( + _("This checks if the minimum clearance between silkscreen\n" + "features and silkscreen features is met.") + ) + form_layout_1.addRow(self.clearance_silk2silk_cb) + + # Copper2silkscreen clearance value + self.clearance_silk2silk_entry = FCEntry() + self.clearance_silk2silk_lbl = QtWidgets.QLabel('%s:' % _("Min value")) + self.clearance_silk2silk_lbl.setToolTip( + _("Minimum acceptable clearance value.") + ) + form_layout_1.addRow(self.clearance_silk2silk_lbl, self.clearance_silk2silk_entry) + + self.s2s = OptionalInputSection( + self.clearance_silk2silk_cb, [self.clearance_silk2silk_lbl, self.clearance_silk2silk_entry]) + + # Silkscreen2soldermask clearance + self.clearance_silk2sm_cb = FCCheckBox('%s:' % _("Silkscreen to soldermask clearance")) + self.clearance_silk2sm_cb.setToolTip( + _("This checks if the minimum clearance between silkscreen\n" + "features and soldermask features is met.") + ) + form_layout_1.addRow(self.clearance_silk2sm_cb) + + # Silkscreen2soldermask clearance value + self.clearance_silk2sm_entry = FCEntry() + self.clearance_silk2sm_lbl = QtWidgets.QLabel('%s:' % _("Min value")) + self.clearance_silk2sm_lbl.setToolTip( + _("Minimum acceptable clearance value.") + ) + form_layout_1.addRow(self.clearance_silk2sm_lbl, self.clearance_silk2sm_entry) + + self.s2sm = OptionalInputSection( + self.clearance_silk2sm_cb, [self.clearance_silk2sm_lbl, self.clearance_silk2sm_entry]) + + # Soldermask2soldermask clearance + self.clearance_sm2sm_cb = FCCheckBox('%s:' % _("Soldermask to soldermask clearance")) + self.clearance_sm2sm_cb.setToolTip( + _("This checks if the minimum clearance between soldermask\n" + "features and soldermask features is met.") + ) + form_layout_1.addRow(self.clearance_sm2sm_cb) + + # Soldermask2soldermask clearance value + self.clearance_sm2sm_entry = FCEntry() + self.clearance_sm2sm_lbl = QtWidgets.QLabel('%s:' % _("Min value")) + self.clearance_sm2sm_lbl.setToolTip( + _("Minimum acceptable clearance value.") + ) + form_layout_1.addRow(self.clearance_sm2sm_lbl, self.clearance_sm2sm_entry) + + self.sm2sm = OptionalInputSection( + self.clearance_sm2sm_cb, [self.clearance_sm2sm_lbl, self.clearance_sm2sm_entry]) + + form_layout_1.addRow(QtWidgets.QLabel("")) + + # Drill2Drill clearance + self.clearance_d2d_cb = FCCheckBox('%s:' % _("Drill hole to drill hole clearance")) + self.clearance_d2d_cb.setToolTip( + _("This checks if the minimum clearance between a drill hole\n" + "and another drill hole is met.") + ) + form_layout_1.addRow(self.clearance_d2d_cb) + + # Drill2Drill clearance value + self.clearance_d2d_entry = FCEntry() + self.clearance_d2d_lbl = QtWidgets.QLabel('%s:' % _("Min value")) + self.clearance_d2d_lbl.setToolTip( + _("Minimum acceptable clearance value.") + ) + form_layout_1.addRow(self.clearance_d2d_lbl, self.clearance_d2d_entry) + + self.d2d = OptionalInputSection( + self.clearance_d2d_cb, [self.clearance_d2d_lbl, self.clearance_d2d_entry]) + + # Ring integrity check + self.ring_integrity_cb = FCCheckBox('%s:' % _("Ring integrity check")) + self.ring_integrity_cb.setToolTip( + _("This checks if the minimum copper ring left by drilling\n" + "a hole into a pad is met.") + ) + form_layout_1.addRow(self.ring_integrity_cb) + + # Ring integrity value + self.ring_integrity_entry = FCEntry() + self.ring_integrity_lbl = QtWidgets.QLabel('%s:' % _("Min value")) + self.ring_integrity_lbl.setToolTip( + _("Minimum acceptable ring value.") + ) + form_layout_1.addRow(self.ring_integrity_lbl, self.ring_integrity_entry) + + self.d2d = OptionalInputSection( + self.ring_integrity_cb, [self.ring_integrity_lbl, self.ring_integrity_entry]) + + # Drill holes overlap check + self.drill_overlap_cb = FCCheckBox('%s:' % _("Drill hole overlap check")) + self.drill_overlap_cb.setToolTip( + _("This checks if drill holes are overlapping\n" + "one over another.") + ) + form_layout_1.addRow(self.drill_overlap_cb) # Buttons hlay_2 = QtWidgets.QHBoxLayout() self.layout.addLayout(hlay_2) - hlay_2.addStretch() - self.panelize_object_button = QtWidgets.QPushButton(_("Panelize Object")) - self.panelize_object_button.setToolTip( + # hlay_2.addStretch() + self.run_button = QtWidgets.QPushButton(_("Run Rules Check")) + self.run_button.setToolTip( _("Panelize the specified object around the specified box.\n" "In other words it creates multiple copies of the source object,\n" "arranged in a 2D array of rows and columns.") ) - hlay_2.addWidget(self.panelize_object_button) + hlay_2.addWidget(self.run_button) self.layout.addStretch() - # Signals - self.reference_radio.activated_custom.connect(self.on_reference_radio_changed) - self.panelize_object_button.clicked.connect(self.on_panelize) - self.type_obj_combo.currentIndexChanged.connect(self.on_type_obj_index_changed) - self.type_box_combo.currentIndexChanged.connect(self.on_type_box_index_changed) + # ####################################################### + # ################ SIGNALS ############################## + # ####################################################### + + # self.app.collection.rowsInserted.connect(self.on_object_loaded) # list to hold the temporary objects self.objs = [] @@ -249,8 +351,11 @@ class RulesCheck(FlatCAMTool): # flag to signal the constrain was activated self.constrain_flag = False + # def on_object_loaded(self, index, row): + # print(index.internalPointer().child_items[row].obj.options['name'], index.data()) + def run(self, toggle=True): - self.app.report_usage("ToolPanelize()") + self.app.report_usage("ToolRulesCheck()") if toggle: # if the splitter is hidden, display it, else hide it but only if the current widget is the same @@ -274,549 +379,485 @@ class RulesCheck(FlatCAMTool): FlatCAMTool.run(self) self.set_tool_ui() - self.app.ui.notebook.setTabText(2, _("Panel. Tool")) + self.app.ui.notebook.setTabText(2, _("Rules Tool")) def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, shortcut='ALT+Z', **kwargs) + FlatCAMTool.install(self, icon, separator, shortcut='ALT+R', **kwargs) def set_tool_ui(self): self.reset_fields() - self.reference_radio.set_value('bbox') - - sp_c = self.app.defaults["tools_panelize_spacing_columns"] if \ - self.app.defaults["tools_panelize_spacing_columns"] else 0.0 - self.spacing_columns.set_value(float(sp_c)) - - sp_r = self.app.defaults["tools_panelize_spacing_rows"] if \ - self.app.defaults["tools_panelize_spacing_rows"] else 0.0 - self.spacing_rows.set_value(float(sp_r)) - - rr = self.app.defaults["tools_panelize_rows"] if \ - self.app.defaults["tools_panelize_rows"] else 0.0 - self.rows.set_value(int(rr)) - - cc = self.app.defaults["tools_panelize_columns"] if \ - self.app.defaults["tools_panelize_columns"] else 0.0 - self.columns.set_value(int(cc)) - - c_cb = self.app.defaults["tools_panelize_constrain"] if \ - self.app.defaults["tools_panelize_constrain"] else False - self.constrain_cb.set_value(c_cb) - - x_w = self.app.defaults["tools_panelize_constrainx"] if \ - self.app.defaults["tools_panelize_constrainx"] else 0.0 - self.x_width_entry.set_value(float(x_w)) - - y_w = self.app.defaults["tools_panelize_constrainy"] if \ - self.app.defaults["tools_panelize_constrainy"] else 0.0 - self.y_height_entry.set_value(float(y_w)) - - panel_type = self.app.defaults["tools_panelize_panel_type"] if \ - self.app.defaults["tools_panelize_panel_type"] else 'gerber' - self.panel_type_radio.set_value(panel_type) - - def on_type_obj_index_changed(self): - obj_type = self.type_obj_combo.currentIndex() - self.object_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex())) - self.object_combo.setCurrentIndex(0) - - # hide the panel type for Excellons, the panel can be only of type Geometry - if self.type_obj_combo.currentText() != 'Excellon': - self.panel_type_label.setDisabled(False) - self.panel_type_radio.setDisabled(False) - else: - self.panel_type_label.setDisabled(True) - self.panel_type_radio.setDisabled(True) - self.panel_type_radio.set_value('geometry') - - def on_type_box_index_changed(self): - obj_type = self.type_box_combo.currentIndex() - self.box_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex())) - self.box_combo.setCurrentIndex(0) - - def on_reference_radio_changed(self, current_val): - if current_val == 'object': - self.type_box_combo.setDisabled(False) - self.type_box_combo_label.setDisabled(False) - self.box_combo.setDisabled(False) - self.box_combo_label.setDisabled(False) - else: - self.type_box_combo.setDisabled(True) - self.type_box_combo_label.setDisabled(True) - self.box_combo.setDisabled(True) - self.box_combo_label.setDisabled(True) - - def on_panelize(self): - name = self.object_combo.currentText() - - # Get source object. - try: - obj = self.app.collection.get_by_name(str(name)) - except Exception as e: - log.debug("Panelize.on_panelize() --> %s" % str(e)) - self.app.inform.emit('[ERROR_NOTCL] %s: %s' % - (_("Could not retrieve object"), name)) - return "Could not retrieve object: %s" % name - - panel_obj = obj - - if panel_obj is None: - self.app.inform.emit('[ERROR_NOTCL] %s: %s' % - (_("Object not found"), panel_obj)) - return "Object not found: %s" % panel_obj - - boxname = self.box_combo.currentText() - - try: - box = self.app.collection.get_by_name(boxname) - except Exception as e: - log.debug("Panelize.on_panelize() --> %s" % str(e)) - self.app.inform.emit('[ERROR_NOTCL] %s: %s' % - (_("Could not retrieve object"), boxname)) - return "Could not retrieve object: %s" % boxname - - if box is None: - self.app.inform.emit('[WARNING_NOTCL]%s: %s' % - (_("No object Box. Using instead"), panel_obj)) - self.reference_radio.set_value('bbox') - - if self.reference_radio.get_value() == 'bbox': - box = panel_obj - - self.outname = name + '_panelized' - - try: - spacing_columns = float(self.spacing_columns.get_value()) - except ValueError: - # try to convert comma to decimal point. if it's still not working error message and return - try: - spacing_columns = float(self.spacing_columns.get_value().replace(',', '.')) - except ValueError: - self.app.inform.emit('[ERROR_NOTCL] %s' % - _("Wrong value format entered, use a number.")) - return - spacing_columns = spacing_columns if spacing_columns is not None else 0 - - try: - spacing_rows = float(self.spacing_rows.get_value()) - except ValueError: - # try to convert comma to decimal point. if it's still not working error message and return - try: - spacing_rows = float(self.spacing_rows.get_value().replace(',', '.')) - except ValueError: - self.app.inform.emit('[ERROR_NOTCL] %s' % - _("Wrong value format entered, use a number.")) - return - spacing_rows = spacing_rows if spacing_rows is not None else 0 - - try: - rows = int(self.rows.get_value()) - except ValueError: - # try to convert comma to decimal point. if it's still not working error message and return - try: - rows = float(self.rows.get_value().replace(',', '.')) - rows = int(rows) - except ValueError: - self.app.inform.emit('[ERROR_NOTCL] %s' % - _("Wrong value format entered, use a number.")) - return - rows = rows if rows is not None else 1 - - try: - columns = int(self.columns.get_value()) - except ValueError: - # try to convert comma to decimal point. if it's still not working error message and return - try: - columns = float(self.columns.get_value().replace(',', '.')) - columns = int(columns) - except ValueError: - self.app.inform.emit('[ERROR_NOTCL] %s' % - _("Wrong value format entered, use a number.")) - return - columns = columns if columns is not None else 1 - - try: - constrain_dx = float(self.x_width_entry.get_value()) - except ValueError: - # try to convert comma to decimal point. if it's still not working error message and return - try: - constrain_dx = float(self.x_width_entry.get_value().replace(',', '.')) - except ValueError: - self.app.inform.emit('[ERROR_NOTCL] %s' % - _("Wrong value format entered, use a number.")) - return - - try: - constrain_dy = float(self.y_height_entry.get_value()) - except ValueError: - # try to convert comma to decimal point. if it's still not working error message and return - try: - constrain_dy = float(self.y_height_entry.get_value().replace(',', '.')) - except ValueError: - self.app.inform.emit('[ERROR_NOTCL] %s' % - _("Wrong value format entered, use a number.")) - return - - panel_type = str(self.panel_type_radio.get_value()) - - if 0 in {columns, rows}: - self.app.inform.emit('[ERROR_NOTCL] %s' % - _("Columns or Rows are zero value. Change them to a positive integer.")) - return "Columns or Rows are zero value. Change them to a positive integer." - - xmin, ymin, xmax, ymax = box.bounds() - lenghtx = xmax - xmin + spacing_columns - lenghty = ymax - ymin + spacing_rows - - # check if constrain within an area is desired - if self.constrain_cb.isChecked(): - panel_lengthx = ((xmax - xmin) * columns) + (spacing_columns * (columns - 1)) - panel_lengthy = ((ymax - ymin) * rows) + (spacing_rows * (rows - 1)) - - # adjust the number of columns and/or rows so the panel will fit within the panel constraint area - if (panel_lengthx > constrain_dx) or (panel_lengthy > constrain_dy): - self.constrain_flag = True - - while panel_lengthx > constrain_dx: - columns -= 1 - panel_lengthx = ((xmax - xmin) * columns) + (spacing_columns * (columns - 1)) - while panel_lengthy > constrain_dy: - rows -= 1 - panel_lengthy = ((ymax - ymin) * rows) + (spacing_rows * (rows - 1)) - - def panelize_2(): - if panel_obj is not None: - self.app.inform.emit(_("Generating panel ... ")) - - self.app.progress.emit(0) - - def job_init_excellon(obj_fin, app_obj): - currenty = 0.0 - self.app.progress.emit(10) - obj_fin.tools = panel_obj.tools.copy() - obj_fin.drills = [] - obj_fin.slots = [] - obj_fin.solid_geometry = [] - - for option in panel_obj.options: - if option is not 'name': - try: - obj_fin.options[option] = panel_obj.options[option] - except KeyError: - log.warning("Failed to copy option. %s" % str(option)) - - geo_len_drills = len(panel_obj.drills) if panel_obj.drills else 0 - geo_len_slots = len(panel_obj.slots) if panel_obj.slots else 0 - - element = 0 - for row in range(rows): - currentx = 0.0 - for col in range(columns): - element += 1 - disp_number = 0 - old_disp_number = 0 - - if panel_obj.drills: - drill_nr = 0 - for tool_dict in panel_obj.drills: - if self.app.abort_flag: - # graceful abort requested by the user - raise FlatCAMApp.GracefulException - - point_offseted = affinity.translate(tool_dict['point'], currentx, currenty) - obj_fin.drills.append( - { - "point": point_offseted, - "tool": tool_dict['tool'] - } - ) - - drill_nr += 1 - disp_number = int(np.interp(drill_nr, [0, geo_len_drills], [0, 100])) - - if disp_number > old_disp_number and disp_number <= 100: - self.app.proc_container.update_view_text(' %s: %d D:%d%%' % - (_("Copy"), - int(element), - disp_number)) - old_disp_number = disp_number - - if panel_obj.slots: - slot_nr = 0 - for tool_dict in panel_obj.slots: - if self.app.abort_flag: - # graceful abort requested by the user - raise FlatCAMApp.GracefulException - - start_offseted = affinity.translate(tool_dict['start'], currentx, currenty) - stop_offseted = affinity.translate(tool_dict['stop'], currentx, currenty) - obj_fin.slots.append( - { - "start": start_offseted, - "stop": stop_offseted, - "tool": tool_dict['tool'] - } - ) - - slot_nr += 1 - disp_number = int(np.interp(slot_nr, [0, geo_len_slots], [0, 100])) - - if disp_number > old_disp_number and disp_number <= 100: - self.app.proc_container.update_view_text(' %s: %d S:%d%%' % - (_("Copy"), - int(element), - disp_number)) - old_disp_number = disp_number - - currentx += lenghtx - currenty += lenghty - - obj_fin.create_geometry() - obj_fin.zeros = panel_obj.zeros - obj_fin.units = panel_obj.units - self.app.proc_container.update_view_text('') - - def job_init_geometry(obj_fin, app_obj): - currentx = 0.0 - currenty = 0.0 - - def translate_recursion(geom): - if type(geom) == list: - geoms = list() - for local_geom in geom: - res_geo = translate_recursion(local_geom) - try: - geoms += res_geo - except TypeError: - geoms.append(res_geo) - return geoms - else: - return affinity.translate(geom, xoff=currentx, yoff=currenty) - - obj_fin.solid_geometry = [] - - # create the initial structure on which to create the panel - if isinstance(panel_obj, FlatCAMGeometry): - obj_fin.multigeo = panel_obj.multigeo - obj_fin.tools = deepcopy(panel_obj.tools) - if panel_obj.multigeo is True: - for tool in panel_obj.tools: - obj_fin.tools[tool]['solid_geometry'][:] = [] - elif isinstance(panel_obj, FlatCAMGerber): - obj_fin.apertures = deepcopy(panel_obj.apertures) - for ap in obj_fin.apertures: - obj_fin.apertures[ap]['geometry'] = list() - - # find the number of polygons in the source solid_geometry - geo_len = 0 - if isinstance(panel_obj, FlatCAMGeometry): - if panel_obj.multigeo is True: - for tool in panel_obj.tools: - try: - for pol in panel_obj.tools[tool]['solid_geometry']: - geo_len += 1 - except TypeError: - geo_len = 1 - else: - try: - for pol in panel_obj.solid_geometry: - geo_len += 1 - except TypeError: - geo_len = 1 - elif isinstance(panel_obj, FlatCAMGerber): - for ap in panel_obj.apertures: - for elem in panel_obj.apertures[ap]['geometry']: - geo_len += 1 - - self.app.progress.emit(0) - element = 0 - for row in range(rows): - currentx = 0.0 - - for col in range(columns): - element += 1 - disp_number = 0 - old_disp_number = 0 - - if isinstance(panel_obj, FlatCAMGeometry): - if panel_obj.multigeo is True: - for tool in panel_obj.tools: - if self.app.abort_flag: - # graceful abort requested by the user - raise FlatCAMApp.GracefulException - - # geo = translate_recursion(panel_obj.tools[tool]['solid_geometry']) - # if isinstance(geo, list): - # obj_fin.tools[tool]['solid_geometry'] += geo - # else: - # obj_fin.tools[tool]['solid_geometry'].append(geo) - - # calculate the number of polygons - geo_len = len(panel_obj.tools[tool]['solid_geometry']) - pol_nr = 0 - for geo_el in panel_obj.tools[tool]['solid_geometry']: - trans_geo = translate_recursion(geo_el) - obj_fin.tools[tool]['solid_geometry'].append(trans_geo) - - pol_nr += 1 - disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100])) - - if old_disp_number < disp_number <= 100: - self.app.proc_container.update_view_text(' %s: %d %d%%' % - (_("Copy"), - int(element), - disp_number)) - old_disp_number = disp_number - else: - # geo = translate_recursion(panel_obj.solid_geometry) - # if isinstance(geo, list): - # obj_fin.solid_geometry += geo - # else: - # obj_fin.solid_geometry.append(geo) - if self.app.abort_flag: - # graceful abort requested by the user - raise FlatCAMApp.GracefulException - - try: - # calculate the number of polygons - geo_len = len(panel_obj.solid_geometry) - except TypeError: - geo_len = 1 - pol_nr = 0 - try: - for geo_el in panel_obj.solid_geometry: - if self.app.abort_flag: - # graceful abort requested by the user - raise FlatCAMApp.GracefulException - - trans_geo = translate_recursion(geo_el) - obj_fin.solid_geometry.append(trans_geo) - - pol_nr += 1 - disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100])) - - if old_disp_number < disp_number <= 100: - self.app.proc_container.update_view_text(' %s: %d %d%%' % - (_("Copy"), - int(element), - disp_number)) - old_disp_number = disp_number - except TypeError: - trans_geo = translate_recursion(panel_obj.solid_geometry) - obj_fin.solid_geometry.append(trans_geo) - else: - # geo = translate_recursion(panel_obj.solid_geometry) - # if isinstance(geo, list): - # obj_fin.solid_geometry += geo - # else: - # obj_fin.solid_geometry.append(geo) - if self.app.abort_flag: - # graceful abort requested by the user - raise FlatCAMApp.GracefulException - - try: - for geo_el in panel_obj.solid_geometry: - if self.app.abort_flag: - # graceful abort requested by the user - raise FlatCAMApp.GracefulException - - trans_geo = translate_recursion(geo_el) - obj_fin.solid_geometry.append(trans_geo) - except TypeError: - trans_geo = translate_recursion(panel_obj.solid_geometry) - obj_fin.solid_geometry.append(trans_geo) - - for apid in panel_obj.apertures: - if self.app.abort_flag: - # graceful abort requested by the user - raise FlatCAMApp.GracefulException - - try: - # calculate the number of polygons - geo_len = len(panel_obj.apertures[apid]['geometry']) - except TypeError: - geo_len = 1 - pol_nr = 0 - for el in panel_obj.apertures[apid]['geometry']: - if self.app.abort_flag: - # graceful abort requested by the user - raise FlatCAMApp.GracefulException - - new_el = dict() - if 'solid' in el: - geo_aper = translate_recursion(el['solid']) - new_el['solid'] = geo_aper - - if 'clear' in el: - geo_aper = translate_recursion(el['clear']) - new_el['clear'] = geo_aper - - if 'follow' in el: - geo_aper = translate_recursion(el['follow']) - new_el['follow'] = geo_aper - - obj_fin.apertures[apid]['geometry'].append(deepcopy(new_el)) - - pol_nr += 1 - disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100])) - - if old_disp_number < disp_number <= 100: - self.app.proc_container.update_view_text(' %s: %d %d%%' % - (_("Copy"), - int(element), - disp_number)) - old_disp_number = disp_number - - currentx += lenghtx - currenty += lenghty - - if panel_type == 'gerber': - self.app.inform.emit('%s' % - _("Generating panel ... Adding the Gerber code.")) - obj_fin.source_file = self.app.export_gerber(obj_name=self.outname, filename=None, - local_use=obj_fin, use_thread=False) - - # app_obj.log.debug("Found %s geometries. Creating a panel geometry cascaded union ..." % - # len(obj_fin.solid_geometry)) - - # obj_fin.solid_geometry = cascaded_union(obj_fin.solid_geometry) - # app_obj.log.debug("Finished creating a cascaded union for the panel.") - self.app.proc_container.update_view_text('') - - self.app.inform.emit('%s: %d' % - (_("Generating panel... Spawning copies"), (int(rows * columns)))) - if isinstance(panel_obj, FlatCAMExcellon): - self.app.progress.emit(50) - self.app.new_object("excellon", self.outname, job_init_excellon, plot=True, autoselected=True) - else: - self.app.progress.emit(50) - self.app.new_object(panel_type, self.outname, job_init_geometry, - plot=True, autoselected=True) - - if self.constrain_flag is False: - self.app.inform.emit('[success] %s' % _("Panel done...")) - else: - self.constrain_flag = False - self.app.inform.emit(_("{text} Too big for the constrain area. " - "Final panel has {col} columns and {row} rows").format( - text='[WARNING] ', col=columns, row=rows)) - - proc = self.app.proc_container.new(_("Working...")) - - def job_thread(app_obj): - try: - panelize_2() - self.app.inform.emit('[success] %s' % _("Panel created successfully.")) - except Exception as ee: - proc.done() - log.debug(str(ee)) - return - proc.done() - - self.app.collection.promise(self.outname) - self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]}) + # def on_panelize(self): + # name = self.object_combo.currentText() + # + # # Get source object. + # try: + # obj = self.app.collection.get_by_name(str(name)) + # except Exception as e: + # log.debug("Panelize.on_panelize() --> %s" % str(e)) + # self.app.inform.emit('[ERROR_NOTCL] %s: %s' % + # (_("Could not retrieve object"), name)) + # return "Could not retrieve object: %s" % name + # + # panel_obj = obj + # + # if panel_obj is None: + # self.app.inform.emit('[ERROR_NOTCL] %s: %s' % + # (_("Object not found"), panel_obj)) + # return "Object not found: %s" % panel_obj + # + # boxname = self.box_combo.currentText() + # + # try: + # box = self.app.collection.get_by_name(boxname) + # except Exception as e: + # log.debug("Panelize.on_panelize() --> %s" % str(e)) + # self.app.inform.emit('[ERROR_NOTCL] %s: %s' % + # (_("Could not retrieve object"), boxname)) + # return "Could not retrieve object: %s" % boxname + # + # if box is None: + # self.app.inform.emit('[WARNING_NOTCL]%s: %s' % + # (_("No object Box. Using instead"), panel_obj)) + # self.reference_radio.set_value('bbox') + # + # if self.reference_radio.get_value() == 'bbox': + # box = panel_obj + # + # self.outname = name + '_panelized' + # + # try: + # spacing_columns = float(self.spacing_columns.get_value()) + # except ValueError: + # # try to convert comma to decimal point. if it's still not working error message and return + # try: + # spacing_columns = float(self.spacing_columns.get_value().replace(',', '.')) + # except ValueError: + # self.app.inform.emit('[ERROR_NOTCL] %s' % + # _("Wrong value format entered, use a number.")) + # return + # spacing_columns = spacing_columns if spacing_columns is not None else 0 + # + # try: + # spacing_rows = float(self.spacing_rows.get_value()) + # except ValueError: + # # try to convert comma to decimal point. if it's still not working error message and return + # try: + # spacing_rows = float(self.spacing_rows.get_value().replace(',', '.')) + # except ValueError: + # self.app.inform.emit('[ERROR_NOTCL] %s' % + # _("Wrong value format entered, use a number.")) + # return + # spacing_rows = spacing_rows if spacing_rows is not None else 0 + # + # try: + # rows = int(self.rows.get_value()) + # except ValueError: + # # try to convert comma to decimal point. if it's still not working error message and return + # try: + # rows = float(self.rows.get_value().replace(',', '.')) + # rows = int(rows) + # except ValueError: + # self.app.inform.emit('[ERROR_NOTCL] %s' % + # _("Wrong value format entered, use a number.")) + # return + # rows = rows if rows is not None else 1 + # + # try: + # columns = int(self.columns.get_value()) + # except ValueError: + # # try to convert comma to decimal point. if it's still not working error message and return + # try: + # columns = float(self.columns.get_value().replace(',', '.')) + # columns = int(columns) + # except ValueError: + # self.app.inform.emit('[ERROR_NOTCL] %s' % + # _("Wrong value format entered, use a number.")) + # return + # columns = columns if columns is not None else 1 + # + # try: + # constrain_dx = float(self.x_width_entry.get_value()) + # except ValueError: + # # try to convert comma to decimal point. if it's still not working error message and return + # try: + # constrain_dx = float(self.x_width_entry.get_value().replace(',', '.')) + # except ValueError: + # self.app.inform.emit('[ERROR_NOTCL] %s' % + # _("Wrong value format entered, use a number.")) + # return + # + # try: + # constrain_dy = float(self.y_height_entry.get_value()) + # except ValueError: + # # try to convert comma to decimal point. if it's still not working error message and return + # try: + # constrain_dy = float(self.y_height_entry.get_value().replace(',', '.')) + # except ValueError: + # self.app.inform.emit('[ERROR_NOTCL] %s' % + # _("Wrong value format entered, use a number.")) + # return + # + # panel_type = str(self.panel_type_radio.get_value()) + # + # if 0 in {columns, rows}: + # self.app.inform.emit('[ERROR_NOTCL] %s' % + # _("Columns or Rows are zero value. Change them to a positive integer.")) + # return "Columns or Rows are zero value. Change them to a positive integer." + # + # xmin, ymin, xmax, ymax = box.bounds() + # lenghtx = xmax - xmin + spacing_columns + # lenghty = ymax - ymin + spacing_rows + # + # # check if constrain within an area is desired + # if self.constrain_cb.isChecked(): + # panel_lengthx = ((xmax - xmin) * columns) + (spacing_columns * (columns - 1)) + # panel_lengthy = ((ymax - ymin) * rows) + (spacing_rows * (rows - 1)) + # + # # adjust the number of columns and/or rows so the panel will fit within the panel constraint area + # if (panel_lengthx > constrain_dx) or (panel_lengthy > constrain_dy): + # self.constrain_flag = True + # + # while panel_lengthx > constrain_dx: + # columns -= 1 + # panel_lengthx = ((xmax - xmin) * columns) + (spacing_columns * (columns - 1)) + # while panel_lengthy > constrain_dy: + # rows -= 1 + # panel_lengthy = ((ymax - ymin) * rows) + (spacing_rows * (rows - 1)) + # + # def panelize_2(): + # if panel_obj is not None: + # self.app.inform.emit(_("Generating panel ... ")) + # + # self.app.progress.emit(0) + # + # def job_init_excellon(obj_fin, app_obj): + # currenty = 0.0 + # self.app.progress.emit(10) + # obj_fin.tools = panel_obj.tools.copy() + # obj_fin.drills = [] + # obj_fin.slots = [] + # obj_fin.solid_geometry = [] + # + # for option in panel_obj.options: + # if option is not 'name': + # try: + # obj_fin.options[option] = panel_obj.options[option] + # except KeyError: + # log.warning("Failed to copy option. %s" % str(option)) + # + # geo_len_drills = len(panel_obj.drills) if panel_obj.drills else 0 + # geo_len_slots = len(panel_obj.slots) if panel_obj.slots else 0 + # + # element = 0 + # for row in range(rows): + # currentx = 0.0 + # for col in range(columns): + # element += 1 + # disp_number = 0 + # old_disp_number = 0 + # + # if panel_obj.drills: + # drill_nr = 0 + # for tool_dict in panel_obj.drills: + # if self.app.abort_flag: + # # graceful abort requested by the user + # raise FlatCAMApp.GracefulException + # + # point_offseted = affinity.translate(tool_dict['point'], currentx, currenty) + # obj_fin.drills.append( + # { + # "point": point_offseted, + # "tool": tool_dict['tool'] + # } + # ) + # + # drill_nr += 1 + # disp_number = int(np.interp(drill_nr, [0, geo_len_drills], [0, 100])) + # + # if disp_number > old_disp_number and disp_number <= 100: + # self.app.proc_container.update_view_text(' %s: %d D:%d%%' % + # (_("Copy"), + # int(element), + # disp_number)) + # old_disp_number = disp_number + # + # if panel_obj.slots: + # slot_nr = 0 + # for tool_dict in panel_obj.slots: + # if self.app.abort_flag: + # # graceful abort requested by the user + # raise FlatCAMApp.GracefulException + # + # start_offseted = affinity.translate(tool_dict['start'], currentx, currenty) + # stop_offseted = affinity.translate(tool_dict['stop'], currentx, currenty) + # obj_fin.slots.append( + # { + # "start": start_offseted, + # "stop": stop_offseted, + # "tool": tool_dict['tool'] + # } + # ) + # + # slot_nr += 1 + # disp_number = int(np.interp(slot_nr, [0, geo_len_slots], [0, 100])) + # + # if disp_number > old_disp_number and disp_number <= 100: + # self.app.proc_container.update_view_text(' %s: %d S:%d%%' % + # (_("Copy"), + # int(element), + # disp_number)) + # old_disp_number = disp_number + # + # currentx += lenghtx + # currenty += lenghty + # + # obj_fin.create_geometry() + # obj_fin.zeros = panel_obj.zeros + # obj_fin.units = panel_obj.units + # self.app.proc_container.update_view_text('') + # + # def job_init_geometry(obj_fin, app_obj): + # currentx = 0.0 + # currenty = 0.0 + # + # def translate_recursion(geom): + # if type(geom) == list: + # geoms = list() + # for local_geom in geom: + # res_geo = translate_recursion(local_geom) + # try: + # geoms += res_geo + # except TypeError: + # geoms.append(res_geo) + # return geoms + # else: + # return affinity.translate(geom, xoff=currentx, yoff=currenty) + # + # obj_fin.solid_geometry = [] + # + # # create the initial structure on which to create the panel + # if isinstance(panel_obj, FlatCAMGeometry): + # obj_fin.multigeo = panel_obj.multigeo + # obj_fin.tools = deepcopy(panel_obj.tools) + # if panel_obj.multigeo is True: + # for tool in panel_obj.tools: + # obj_fin.tools[tool]['solid_geometry'][:] = [] + # elif isinstance(panel_obj, FlatCAMGerber): + # obj_fin.apertures = deepcopy(panel_obj.apertures) + # for ap in obj_fin.apertures: + # obj_fin.apertures[ap]['geometry'] = list() + # + # # find the number of polygons in the source solid_geometry + # geo_len = 0 + # if isinstance(panel_obj, FlatCAMGeometry): + # if panel_obj.multigeo is True: + # for tool in panel_obj.tools: + # try: + # for pol in panel_obj.tools[tool]['solid_geometry']: + # geo_len += 1 + # except TypeError: + # geo_len = 1 + # else: + # try: + # for pol in panel_obj.solid_geometry: + # geo_len += 1 + # except TypeError: + # geo_len = 1 + # elif isinstance(panel_obj, FlatCAMGerber): + # for ap in panel_obj.apertures: + # for elem in panel_obj.apertures[ap]['geometry']: + # geo_len += 1 + # + # self.app.progress.emit(0) + # element = 0 + # for row in range(rows): + # currentx = 0.0 + # + # for col in range(columns): + # element += 1 + # disp_number = 0 + # old_disp_number = 0 + # + # if isinstance(panel_obj, FlatCAMGeometry): + # if panel_obj.multigeo is True: + # for tool in panel_obj.tools: + # if self.app.abort_flag: + # # graceful abort requested by the user + # raise FlatCAMApp.GracefulException + # + # # geo = translate_recursion(panel_obj.tools[tool]['solid_geometry']) + # # if isinstance(geo, list): + # # obj_fin.tools[tool]['solid_geometry'] += geo + # # else: + # # obj_fin.tools[tool]['solid_geometry'].append(geo) + # + # # calculate the number of polygons + # geo_len = len(panel_obj.tools[tool]['solid_geometry']) + # pol_nr = 0 + # for geo_el in panel_obj.tools[tool]['solid_geometry']: + # trans_geo = translate_recursion(geo_el) + # obj_fin.tools[tool]['solid_geometry'].append(trans_geo) + # + # pol_nr += 1 + # disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100])) + # + # if old_disp_number < disp_number <= 100: + # self.app.proc_container.update_view_text(' %s: %d %d%%' % + # (_("Copy"), + # int(element), + # disp_number)) + # old_disp_number = disp_number + # else: + # # geo = translate_recursion(panel_obj.solid_geometry) + # # if isinstance(geo, list): + # # obj_fin.solid_geometry += geo + # # else: + # # obj_fin.solid_geometry.append(geo) + # if self.app.abort_flag: + # # graceful abort requested by the user + # raise FlatCAMApp.GracefulException + # + # try: + # # calculate the number of polygons + # geo_len = len(panel_obj.solid_geometry) + # except TypeError: + # geo_len = 1 + # pol_nr = 0 + # try: + # for geo_el in panel_obj.solid_geometry: + # if self.app.abort_flag: + # # graceful abort requested by the user + # raise FlatCAMApp.GracefulException + # + # trans_geo = translate_recursion(geo_el) + # obj_fin.solid_geometry.append(trans_geo) + # + # pol_nr += 1 + # disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100])) + # + # if old_disp_number < disp_number <= 100: + # self.app.proc_container.update_view_text(' %s: %d %d%%' % + # (_("Copy"), + # int(element), + # disp_number)) + # old_disp_number = disp_number + # except TypeError: + # trans_geo = translate_recursion(panel_obj.solid_geometry) + # obj_fin.solid_geometry.append(trans_geo) + # else: + # # geo = translate_recursion(panel_obj.solid_geometry) + # # if isinstance(geo, list): + # # obj_fin.solid_geometry += geo + # # else: + # # obj_fin.solid_geometry.append(geo) + # if self.app.abort_flag: + # # graceful abort requested by the user + # raise FlatCAMApp.GracefulException + # + # try: + # for geo_el in panel_obj.solid_geometry: + # if self.app.abort_flag: + # # graceful abort requested by the user + # raise FlatCAMApp.GracefulException + # + # trans_geo = translate_recursion(geo_el) + # obj_fin.solid_geometry.append(trans_geo) + # except TypeError: + # trans_geo = translate_recursion(panel_obj.solid_geometry) + # obj_fin.solid_geometry.append(trans_geo) + # + # for apid in panel_obj.apertures: + # if self.app.abort_flag: + # # graceful abort requested by the user + # raise FlatCAMApp.GracefulException + # + # try: + # # calculate the number of polygons + # geo_len = len(panel_obj.apertures[apid]['geometry']) + # except TypeError: + # geo_len = 1 + # pol_nr = 0 + # for el in panel_obj.apertures[apid]['geometry']: + # if self.app.abort_flag: + # # graceful abort requested by the user + # raise FlatCAMApp.GracefulException + # + # new_el = dict() + # if 'solid' in el: + # geo_aper = translate_recursion(el['solid']) + # new_el['solid'] = geo_aper + # + # if 'clear' in el: + # geo_aper = translate_recursion(el['clear']) + # new_el['clear'] = geo_aper + # + # if 'follow' in el: + # geo_aper = translate_recursion(el['follow']) + # new_el['follow'] = geo_aper + # + # obj_fin.apertures[apid]['geometry'].append(deepcopy(new_el)) + # + # pol_nr += 1 + # disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100])) + # + # if old_disp_number < disp_number <= 100: + # self.app.proc_container.update_view_text(' %s: %d %d%%' % + # (_("Copy"), + # int(element), + # disp_number)) + # old_disp_number = disp_number + # + # currentx += lenghtx + # currenty += lenghty + # + # if panel_type == 'gerber': + # self.app.inform.emit('%s' % + # _("Generating panel ... Adding the Gerber code.")) + # obj_fin.source_file = self.app.export_gerber(obj_name=self.outname, filename=None, + # local_use=obj_fin, use_thread=False) + # + # # app_obj.log.debug("Found %s geometries. Creating a panel geometry cascaded union ..." % + # # len(obj_fin.solid_geometry)) + # + # # obj_fin.solid_geometry = cascaded_union(obj_fin.solid_geometry) + # # app_obj.log.debug("Finished creating a cascaded union for the panel.") + # self.app.proc_container.update_view_text('') + # + # self.app.inform.emit('%s: %d' % + # (_("Generating panel... Spawning copies"), (int(rows * columns)))) + # if isinstance(panel_obj, FlatCAMExcellon): + # self.app.progress.emit(50) + # self.app.new_object("excellon", self.outname, job_init_excellon, plot=True, autoselected=True) + # else: + # self.app.progress.emit(50) + # self.app.new_object(panel_type, self.outname, job_init_geometry, + # plot=True, autoselected=True) + # + # if self.constrain_flag is False: + # self.app.inform.emit('[success] %s' % _("Panel done...")) + # else: + # self.constrain_flag = False + # self.app.inform.emit(_("{text} Too big for the constrain area. " + # "Final panel has {col} columns and {row} rows").format( + # text='[WARNING] ', col=columns, row=rows)) + # + # proc = self.app.proc_container.new(_("Working...")) + # + # def job_thread(app_obj): + # try: + # panelize_2() + # self.app.inform.emit('[success] %s' % _("Panel created successfully.")) + # except Exception as ee: + # proc.done() + # log.debug(str(ee)) + # return + # proc.done() + # + # self.app.collection.promise(self.outname) + # self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]}) def reset_fields(self): - self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) - self.box_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) + # self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) + # self.box_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) + pass diff --git a/flatcamTools/ToolTransform.py b/flatcamTools/ToolTransform.py index 04ee533c..dfb43f86 100644 --- a/flatcamTools/ToolTransform.py +++ b/flatcamTools/ToolTransform.py @@ -403,7 +403,7 @@ class ToolTransform(FlatCAMTool): self.app.ui.notebook.setTabText(2, _("Transform Tool")) def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, shortcut='ALT+R', **kwargs) + FlatCAMTool.install(self, icon, separator, shortcut='ALT+E', **kwargs) def set_tool_ui(self): # ## Initialize form diff --git a/share/open_script32.png b/share/open_script32.png new file mode 100644 index 00000000..e8b62c7e Binary files /dev/null and b/share/open_script32.png differ diff --git a/share/rules32.png b/share/rules32.png new file mode 100644 index 00000000..ba558b24 Binary files /dev/null and b/share/rules32.png differ