from PyQt4 import QtGui, QtCore from shapely.geometry import Point from shapely import affinity from math import sqrt import FlatCAMApp from GUIElements import * from FlatCAMObj import FlatCAMGerber, FlatCAMExcellon class FlatCAMTool(QtGui.QWidget): toolName = "FlatCAM Generic Tool" def __init__(self, app, parent=None): """ :param app: The application this tool will run in. :type app: App :param parent: Qt Parent :return: FlatCAMTool """ QtGui.QWidget.__init__(self, parent) # self.setSizePolicy(QtGui.QSizePolicy.Maximum, QtGui.QSizePolicy.Maximum) self.layout = QtGui.QVBoxLayout() self.setLayout(self.layout) self.app = app self.menuAction = None def install(self): self.menuAction = self.app.ui.menutool.addAction(self.toolName) self.menuAction.triggered.connect(self.run) def run(self): # Remove anything else in the GUI self.app.ui.tool_scroll_area.takeWidget() # Put ourself in the GUI self.app.ui.tool_scroll_area.setWidget(self) # Switch notebook to tool page self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab) self.show() class DblSidedTool(FlatCAMTool): toolName = "Double-Sided PCB Tool" def __init__(self, app): FlatCAMTool.__init__(self, app) ## Title title_label = QtGui.QLabel("%s" % self.toolName) self.layout.addWidget(title_label) ## Form Layout form_layout = QtGui.QFormLayout() self.layout.addLayout(form_layout) ## Layer to mirror self.object_combo = QtGui.QComboBox() self.object_combo.setModel(self.app.collection) form_layout.addRow("Bottom Layer:", self.object_combo) ## Axis self.mirror_axis = RadioSet([{'label': 'X', 'value': 'X'}, {'label': 'Y', 'value': 'Y'}]) form_layout.addRow("Mirror Axis:", self.mirror_axis) ## Axis Location self.axis_location = RadioSet([{'label': 'Point', 'value': 'point'}, {'label': 'Box', 'value': 'box'}]) form_layout.addRow("Axis Location:", self.axis_location) ## Point/Box self.point_box_container = QtGui.QVBoxLayout() form_layout.addRow("Point/Box:", self.point_box_container) self.point = EvalEntry() self.point_box_container.addWidget(self.point) self.box_combo = QtGui.QComboBox() self.box_combo.setModel(self.app.collection) self.point_box_container.addWidget(self.box_combo) self.box_combo.hide() ## Alignment holes self.alignment_holes = EvalEntry() form_layout.addRow("Alignment Holes:", self.alignment_holes) ## Drill diameter for alignment holes self.drill_dia = LengthEntry() form_layout.addRow("Drill diam.:", self.drill_dia) ## Buttons hlay = QtGui.QHBoxLayout() self.layout.addLayout(hlay) hlay.addStretch() self.create_alignment_hole_button = QtGui.QPushButton("Create Alignment Drill") self.mirror_object_button = QtGui.QPushButton("Mirror Object") hlay.addWidget(self.create_alignment_hole_button) hlay.addWidget(self.mirror_object_button) self.layout.addStretch() ## Signals self.create_alignment_hole_button.clicked.connect(self.on_create_alignment_holes) self.mirror_object_button.clicked.connect(self.on_mirror) self.axis_location.group_toggle_fn = self.on_toggle_pointbox ## Initialize form self.mirror_axis.set_value('X') self.axis_location.set_value('point') def on_create_alignment_holes(self): axis = self.mirror_axis.get_value() mode = self.axis_location.get_value() if mode == "point": px, py = self.point.get_value() else: selection_index = self.box_combo.currentIndex() bb_obj = self.app.collection.object_list[selection_index] # TODO: Direct access?? xmin, ymin, xmax, ymax = bb_obj.bounds() px = 0.5*(xmin+xmax) py = 0.5*(ymin+ymax) xscale, yscale = {"X": (1.0, -1.0), "Y": (-1.0, 1.0)}[axis] dia = self.drill_dia.get_value() tools = {"1": {"C": dia}} holes = self.alignment_holes.get_value() drills = [] for hole in holes: point = Point(hole) point_mirror = affinity.scale(point, xscale, yscale, origin=(px, py)) drills.append({"point": point, "tool": "1"}) drills.append({"point": point_mirror, "tool": "1"}) def obj_init(obj_inst, app_inst): obj_inst.tools = tools obj_inst.drills = drills obj_inst.create_geometry() self.app.new_object("excellon", "Alignment Drills", obj_init) def on_mirror(self): selection_index = self.object_combo.currentIndex() fcobj = self.app.collection.object_list[selection_index] # For now, lets limit to Gerbers and Excellons. # assert isinstance(gerb, FlatCAMGerber) if not isinstance(fcobj, FlatCAMGerber) and not isinstance(fcobj, FlatCAMExcellon): self.info("ERROR: Only Gerber and Excellon objects can be mirrored.") return axis = self.mirror_axis.get_value() mode = self.axis_location.get_value() if mode == "point": px, py = self.point.get_value() else: selection_index = self.box_combo.currentIndex() bb_obj = self.app.collection.object_list[selection_index] # TODO: Direct access?? xmin, ymin, xmax, ymax = bb_obj.bounds() px = 0.5*(xmin+xmax) py = 0.5*(ymin+ymax) fcobj.mirror(axis, [px, py]) fcobj.plot() def on_toggle_pointbox(self): if self.axis_location.get_value() == "point": self.point.show() self.box_combo.hide() else: self.point.hide() self.box_combo.show() class Measurement(FlatCAMTool): toolName = "Measurement Tool" def __init__(self, app): FlatCAMTool.__init__(self, app) # self.setContentsMargins(0, 0, 0, 0) self.layout.setMargin(0) self.layout.setContentsMargins(0, 0, 3, 0) self.setSizePolicy(QtGui.QSizePolicy.Ignored, QtGui.QSizePolicy.Maximum) self.point1 = None self.point2 = None self.label = QtGui.QLabel("Click on a reference point ...") self.label.setFrameStyle(QtGui.QFrame.StyledPanel | QtGui.QFrame.Plain) self.label.setMargin(3) self.layout.addWidget(self.label) # self.layout.setMargin(0) self.setVisible(False) self.click_subscription = None self.move_subscription = None def install(self): FlatCAMTool.install(self) self.app.ui.right_layout.addWidget(self) self.app.plotcanvas.mpl_connect('key_press_event', self.on_key_press) def run(self): self.toggle() def on_click(self, event): if self.point1 is None: self.point1 = (event.xdata, event.ydata) else: self.point2 = copy(self.point1) self.point1 = (event.xdata, event.ydata) self.on_move(event) def on_key_press(self, event): if event.key == 'r': self.toggle() def toggle(self): if self.isVisible(): self.setVisible(False) self.app.plotcanvas.mpl_disconnect(self.move_subscription) self.app.plotcanvas.mpl_disconnect(self.click_subscription) else: self.setVisible(True) self.move_subscription = self.app.plotcanvas.mpl_connect('motion_notify_event', self.on_move) self.click_subscription = self.app.plotcanvas.mpl_connect('button_press_event', self.on_click) def on_move(self, event): if self.point1 is None: self.label.setText("Click on a reference point...") else: try: dx = event.xdata - self.point1[0] dy = event.ydata - self.point1[1] d = sqrt(dx**2 + dy**2) self.label.setText("D = %.4f D(x) = %.4f D(y) = %.4f" % (d, dx, dy)) except TypeError: pass if self.update is not None: self.update()