FlatCAMTool.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. from PyQt4 import QtGui, QtCore
  2. from shapely.geometry import Point
  3. from shapely import affinity
  4. from math import sqrt
  5. import FlatCAMApp
  6. from GUIElements import *
  7. from FlatCAMObj import FlatCAMGerber, FlatCAMExcellon
  8. class FlatCAMTool(QtGui.QWidget):
  9. toolName = "FlatCAM Generic Tool"
  10. def __init__(self, app, parent=None):
  11. """
  12. :param app: The application this tool will run in.
  13. :type app: App
  14. :param parent: Qt Parent
  15. :return: FlatCAMTool
  16. """
  17. QtGui.QWidget.__init__(self, parent)
  18. # self.setSizePolicy(QtGui.QSizePolicy.Maximum, QtGui.QSizePolicy.Maximum)
  19. self.layout = QtGui.QVBoxLayout()
  20. self.setLayout(self.layout)
  21. self.app = app
  22. self.menuAction = None
  23. def install(self):
  24. self.menuAction = self.app.ui.menutool.addAction(self.toolName)
  25. self.menuAction.triggered.connect(self.run)
  26. def run(self):
  27. # Remove anything else in the GUI
  28. self.app.ui.tool_scroll_area.takeWidget()
  29. # Put ourself in the GUI
  30. self.app.ui.tool_scroll_area.setWidget(self)
  31. # Switch notebook to tool page
  32. self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
  33. self.show()
  34. class DblSidedTool(FlatCAMTool):
  35. toolName = "Double-Sided PCB Tool"
  36. def __init__(self, app):
  37. FlatCAMTool.__init__(self, app)
  38. ## Title
  39. title_label = QtGui.QLabel("<font size=4><b>%s</b></font>" % self.toolName)
  40. self.layout.addWidget(title_label)
  41. ## Form Layout
  42. form_layout = QtGui.QFormLayout()
  43. self.layout.addLayout(form_layout)
  44. ## Layer to mirror
  45. self.object_combo = QtGui.QComboBox()
  46. self.object_combo.setModel(self.app.collection)
  47. form_layout.addRow("Bottom Layer:", self.object_combo)
  48. ## Axis
  49. self.mirror_axis = RadioSet([{'label': 'X', 'value': 'X'},
  50. {'label': 'Y', 'value': 'Y'}])
  51. form_layout.addRow("Mirror Axis:", self.mirror_axis)
  52. ## Axis Location
  53. self.axis_location = RadioSet([{'label': 'Point', 'value': 'point'},
  54. {'label': 'Box', 'value': 'box'}])
  55. form_layout.addRow("Axis Location:", self.axis_location)
  56. ## Point/Box
  57. self.point_box_container = QtGui.QVBoxLayout()
  58. form_layout.addRow("Point/Box:", self.point_box_container)
  59. self.point = EvalEntry()
  60. self.point_box_container.addWidget(self.point)
  61. self.box_combo = QtGui.QComboBox()
  62. self.box_combo.setModel(self.app.collection)
  63. self.point_box_container.addWidget(self.box_combo)
  64. self.box_combo.hide()
  65. ## Alignment holes
  66. self.alignment_holes = EvalEntry()
  67. form_layout.addRow("Alignment Holes:", self.alignment_holes)
  68. ## Drill diameter for alignment holes
  69. self.drill_dia = LengthEntry()
  70. form_layout.addRow("Drill diam.:", self.drill_dia)
  71. ## Buttons
  72. hlay = QtGui.QHBoxLayout()
  73. self.layout.addLayout(hlay)
  74. hlay.addStretch()
  75. self.create_alignment_hole_button = QtGui.QPushButton("Create Alignment Drill")
  76. self.mirror_object_button = QtGui.QPushButton("Mirror Object")
  77. hlay.addWidget(self.create_alignment_hole_button)
  78. hlay.addWidget(self.mirror_object_button)
  79. self.layout.addStretch()
  80. ## Signals
  81. self.create_alignment_hole_button.clicked.connect(self.on_create_alignment_holes)
  82. self.mirror_object_button.clicked.connect(self.on_mirror)
  83. self.axis_location.group_toggle_fn = self.on_toggle_pointbox
  84. ## Initialize form
  85. self.mirror_axis.set_value('X')
  86. self.axis_location.set_value('point')
  87. def on_create_alignment_holes(self):
  88. axis = self.mirror_axis.get_value()
  89. mode = self.axis_location.get_value()
  90. if mode == "point":
  91. px, py = self.point.get_value()
  92. else:
  93. selection_index = self.box_combo.currentIndex()
  94. bb_obj = self.app.collection.object_list[selection_index] # TODO: Direct access??
  95. xmin, ymin, xmax, ymax = bb_obj.bounds()
  96. px = 0.5*(xmin+xmax)
  97. py = 0.5*(ymin+ymax)
  98. xscale, yscale = {"X": (1.0, -1.0), "Y": (-1.0, 1.0)}[axis]
  99. dia = self.drill_dia.get_value()
  100. tools = {"1": {"C": dia}}
  101. holes = self.alignment_holes.get_value()
  102. drills = []
  103. for hole in holes:
  104. point = Point(hole)
  105. point_mirror = affinity.scale(point, xscale, yscale, origin=(px, py))
  106. drills.append({"point": point, "tool": "1"})
  107. drills.append({"point": point_mirror, "tool": "1"})
  108. def obj_init(obj_inst, app_inst):
  109. obj_inst.tools = tools
  110. obj_inst.drills = drills
  111. obj_inst.create_geometry()
  112. self.app.new_object("excellon", "Alignment Drills", obj_init)
  113. def on_mirror(self):
  114. selection_index = self.object_combo.currentIndex()
  115. fcobj = self.app.collection.object_list[selection_index]
  116. # For now, lets limit to Gerbers and Excellons.
  117. # assert isinstance(gerb, FlatCAMGerber)
  118. if not isinstance(fcobj, FlatCAMGerber) and not isinstance(fcobj, FlatCAMExcellon):
  119. self.info("ERROR: Only Gerber and Excellon objects can be mirrored.")
  120. return
  121. axis = self.mirror_axis.get_value()
  122. mode = self.axis_location.get_value()
  123. if mode == "point":
  124. px, py = self.point.get_value()
  125. else:
  126. selection_index = self.box_combo.currentIndex()
  127. bb_obj = self.app.collection.object_list[selection_index] # TODO: Direct access??
  128. xmin, ymin, xmax, ymax = bb_obj.bounds()
  129. px = 0.5*(xmin+xmax)
  130. py = 0.5*(ymin+ymax)
  131. fcobj.mirror(axis, [px, py])
  132. fcobj.plot()
  133. def on_toggle_pointbox(self):
  134. if self.axis_location.get_value() == "point":
  135. self.point.show()
  136. self.box_combo.hide()
  137. else:
  138. self.point.hide()
  139. self.box_combo.show()
  140. class Measurement(FlatCAMTool):
  141. toolName = "Measurement Tool"
  142. def __init__(self, app):
  143. FlatCAMTool.__init__(self, app)
  144. # self.setContentsMargins(0, 0, 0, 0)
  145. self.layout.setMargin(0)
  146. self.layout.setContentsMargins(0, 0, 3, 0)
  147. self.setSizePolicy(QtGui.QSizePolicy.Ignored, QtGui.QSizePolicy.Maximum)
  148. self.point1 = None
  149. self.point2 = None
  150. self.label = QtGui.QLabel("Click on a reference point ...")
  151. self.label.setFrameStyle(QtGui.QFrame.StyledPanel | QtGui.QFrame.Plain)
  152. self.label.setMargin(3)
  153. self.layout.addWidget(self.label)
  154. # self.layout.setMargin(0)
  155. self.setVisible(False)
  156. self.click_subscription = None
  157. self.move_subscription = None
  158. def install(self):
  159. FlatCAMTool.install(self)
  160. self.app.ui.right_layout.addWidget(self)
  161. self.app.plotcanvas.mpl_connect('key_press_event', self.on_key_press)
  162. def run(self):
  163. self.toggle()
  164. def on_click(self, event):
  165. if self.point1 is None:
  166. self.point1 = (event.xdata, event.ydata)
  167. else:
  168. self.point2 = copy(self.point1)
  169. self.point1 = (event.xdata, event.ydata)
  170. self.on_move(event)
  171. def on_key_press(self, event):
  172. if event.key == 'm':
  173. self.toggle()
  174. def toggle(self):
  175. if self.isVisible():
  176. self.setVisible(False)
  177. self.app.plotcanvas.mpl_disconnect(self.move_subscription)
  178. self.app.plotcanvas.mpl_disconnect(self.click_subscription)
  179. else:
  180. self.setVisible(True)
  181. self.move_subscription = self.app.plotcanvas.mpl_connect('motion_notify_event', self.on_move)
  182. self.click_subscription = self.app.plotcanvas.mpl_connect('button_press_event', self.on_click)
  183. def on_move(self, event):
  184. if self.point1 is None:
  185. self.label.setText("Click on a reference point...")
  186. else:
  187. try:
  188. dx = event.xdata - self.point1[0]
  189. dy = event.ydata - self.point1[1]
  190. d = sqrt(dx**2 + dy**2)
  191. self.label.setText("D = %.4f D(x) = %.4f D(y) = %.4f" % (d, dx, dy))
  192. except TypeError:
  193. pass
  194. if self.update is not None:
  195. self.update()