AppTool.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. # ########################################################## ##
  2. # FlatCAM: 2D Post-processing for Manufacturing #
  3. # http://flatcam.org #
  4. # Author: Juan Pablo Caram (c) #
  5. # Date: 2/5/2014 #
  6. # MIT Licence #
  7. # ########################################################## ##
  8. from PyQt5 import QtCore, QtWidgets
  9. from shapely.geometry import Polygon, LineString
  10. import gettext
  11. import AppTranslation as fcTranslate
  12. import builtins
  13. fcTranslate.apply_language('strings')
  14. if '_' not in builtins.__dict__:
  15. _ = gettext.gettext
  16. class AppTool(QtWidgets.QWidget):
  17. toolName = "FlatCAM Generic Tool"
  18. def __init__(self, app, parent=None):
  19. """
  20. :param app: The application this tool will run in.
  21. :type app: App_Main.App
  22. :param parent: Qt Parent
  23. :return: AppTool
  24. """
  25. QtWidgets.QWidget.__init__(self, parent)
  26. self.app = app
  27. self.decimals = self.app.decimals
  28. # self.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
  29. self.layout = QtWidgets.QVBoxLayout()
  30. self.setLayout(self.layout)
  31. self.menuAction = None
  32. def install(self, icon=None, separator=None, shortcut=None, **kwargs):
  33. before = None
  34. # 'pos' is the menu where the Action has to be installed
  35. # if no 'pos' kwarg is provided then by default our Action will be installed in the menutool
  36. # as it previously was
  37. if 'pos' in kwargs:
  38. pos = kwargs['pos']
  39. else:
  40. pos = self.app.ui.menutool
  41. # 'before' is the Action in the menu stated by 'pos' kwarg, before which we want our Action to be installed
  42. # if 'before' kwarg is not provided, by default our Action will be added in the last place.
  43. if 'before' in kwargs:
  44. before = (kwargs['before'])
  45. # create the new Action
  46. self.menuAction = QtWidgets.QAction(self)
  47. # if provided, add an icon to this Action
  48. if icon is not None:
  49. self.menuAction.setIcon(icon)
  50. # set the text name of the Action, which will be displayed in the menu
  51. if shortcut is None:
  52. self.menuAction.setText(self.toolName)
  53. else:
  54. self.menuAction.setText(self.toolName + '\t%s' % shortcut)
  55. # add a ToolTip to the new Action
  56. # self.menuAction.setToolTip(self.toolTip) # currently not available
  57. # insert the action in the position specified by 'before' and 'pos' kwargs
  58. pos.insertAction(before, self.menuAction)
  59. # if separator parameter is True add a Separator after the newly created Action
  60. if separator is True:
  61. pos.addSeparator()
  62. self.menuAction.triggered.connect(self.run)
  63. def run(self):
  64. if self.app.tool_tab_locked is True:
  65. return
  66. # Remove anything else in the AppGUI
  67. self.app.ui.tool_scroll_area.takeWidget()
  68. # Put ourself in the AppGUI
  69. self.app.ui.tool_scroll_area.setWidget(self)
  70. # Switch notebook to tool page
  71. self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
  72. # Set the tool name as the widget object name
  73. self.app.ui.tool_scroll_area.widget().setObjectName(self.toolName)
  74. self.show()
  75. @classmethod
  76. def draw_tool_selection_shape(cls, old_coords, coords, **kwargs):
  77. """
  78. :param old_coords: old coordinates
  79. :param coords: new coordinates
  80. :param kwargs:
  81. :return:
  82. """
  83. if 'shapes_storage' in kwargs:
  84. s_storage = kwargs['shapes_storage']
  85. else:
  86. s_storage = cls.app.tool_shapes
  87. if 'color' in kwargs:
  88. color = kwargs['color']
  89. else:
  90. color = cls.app.defaults['global_sel_line']
  91. if 'face_color' in kwargs:
  92. face_color = kwargs['face_color']
  93. else:
  94. face_color = cls.app.defaults['global_sel_fill']
  95. if 'face_alpha' in kwargs:
  96. face_alpha = kwargs['face_alpha']
  97. else:
  98. face_alpha = 0.3
  99. x0, y0 = old_coords
  100. x1, y1 = coords
  101. pt1 = (x0, y0)
  102. pt2 = (x1, y0)
  103. pt3 = (x1, y1)
  104. pt4 = (x0, y1)
  105. sel_rect = Polygon([pt1, pt2, pt3, pt4])
  106. # color_t = Color(face_color)
  107. # color_t.alpha = face_alpha
  108. color_t = face_color[:-2] + str(hex(int(face_alpha * 255)))[2:]
  109. s_storage.add(sel_rect, color=color, face_color=color_t, update=True, layer=0, tolerance=None)
  110. if cls.app.is_legacy is True:
  111. s_storage.redraw()
  112. @classmethod
  113. def draw_selection_shape_polygon(cls, points, **kwargs):
  114. """
  115. :param points: a list of points from which to create a Polygon
  116. :param kwargs:
  117. :return:
  118. """
  119. if 'shapes_storage' in kwargs:
  120. s_storage = kwargs['shapes_storage']
  121. else:
  122. s_storage = cls.app.tool_shapes
  123. if 'color' in kwargs:
  124. color = kwargs['color']
  125. else:
  126. color = cls.app.defaults['global_sel_line']
  127. if 'face_color' in kwargs:
  128. face_color = kwargs['face_color']
  129. else:
  130. face_color = cls.app.defaults['global_sel_fill']
  131. if 'face_alpha' in kwargs:
  132. face_alpha = kwargs['face_alpha']
  133. else:
  134. face_alpha = 0.3
  135. if len(points) < 3:
  136. sel_rect = LineString(points)
  137. else:
  138. sel_rect = Polygon(points)
  139. # color_t = Color(face_color)
  140. # color_t.alpha = face_alpha
  141. color_t = face_color[:-2] + str(hex(int(face_alpha * 255)))[2:]
  142. s_storage.add(sel_rect, color=color, face_color=color_t, update=True, layer=0, tolerance=None)
  143. if cls.app.is_legacy is True:
  144. s_storage.redraw()
  145. @classmethod
  146. def delete_tool_selection_shape(cls, **kwargs):
  147. """
  148. :param kwargs:
  149. :return:
  150. """
  151. if 'shapes_storage' in kwargs:
  152. s_storage = kwargs['shapes_storage']
  153. else:
  154. s_storage = cls.app.tool_shapes
  155. s_storage.clear()
  156. s_storage.redraw()
  157. @classmethod
  158. def draw_moving_selection_shape_poly(cls, points, data, **kwargs):
  159. """
  160. :param points:
  161. :param data:
  162. :param kwargs:
  163. :return:
  164. """
  165. if 'shapes_storage' in kwargs:
  166. s_storage = kwargs['shapes_storage']
  167. else:
  168. s_storage = cls.app.move_tool.sel_shapes
  169. if 'color' in kwargs:
  170. color = kwargs['color']
  171. else:
  172. color = cls.app.defaults['global_sel_line']
  173. if 'face_color' in kwargs:
  174. face_color = kwargs['face_color']
  175. else:
  176. face_color = cls.app.defaults['global_sel_fill']
  177. if 'face_alpha' in kwargs:
  178. face_alpha = kwargs['face_alpha']
  179. else:
  180. face_alpha = 0.3
  181. temp_points = [x for x in points]
  182. try:
  183. if data != temp_points[-1]:
  184. temp_points.append(data)
  185. except IndexError:
  186. return
  187. l_points = len(temp_points)
  188. if l_points == 2:
  189. geo = LineString(temp_points)
  190. elif l_points > 2:
  191. geo = Polygon(temp_points)
  192. else:
  193. return
  194. color_t = face_color[:-2] + str(hex(int(face_alpha * 255)))[2:]
  195. color_t_error = "#00000000"
  196. if geo.is_valid and not geo.is_empty:
  197. s_storage.add(geo, color=color, face_color=color_t, update=True, layer=0, tolerance=None)
  198. elif not geo.is_valid:
  199. s_storage.add(geo, color="red", face_color=color_t_error, update=True, layer=0, tolerance=None)
  200. if cls.app.is_legacy is True:
  201. s_storage.redraw()
  202. @classmethod
  203. def delete_moving_selection_shape(cls, **kwargs):
  204. """
  205. :param kwargs:
  206. :return:
  207. """
  208. if 'shapes_storage' in kwargs:
  209. s_storage = kwargs['shapes_storage']
  210. else:
  211. s_storage = cls.app.move_tool.sel_shapes
  212. s_storage.clear()
  213. s_storage.redraw()
  214. def confirmation_message(self, accepted, minval, maxval):
  215. if accepted is False:
  216. self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' % (_("Edited value is out of range"),
  217. self.decimals,
  218. minval,
  219. self.decimals,
  220. maxval), False)
  221. else:
  222. self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
  223. def confirmation_message_int(self, accepted, minval, maxval):
  224. if accepted is False:
  225. self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' %
  226. (_("Edited value is out of range"), minval, maxval), False)
  227. else:
  228. self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
  229. def sizeHint(self):
  230. """
  231. I've overloaded this just in case I will need to make changes in the future to enforce dimensions
  232. :return:
  233. """
  234. default_hint_size = super(AppTool, self).sizeHint()
  235. return QtCore.QSize(default_hint_size.width(), default_hint_size.height())