ToolFilm.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. # ########################################################## ##
  2. # FlatCAM: 2D Post-processing for Manufacturing #
  3. # http://flatcam.org #
  4. # File Author: Marius Adrian Stanciu (c) #
  5. # Date: 3/10/2019 #
  6. # MIT Licence #
  7. # ########################################################## ##
  8. from FlatCAMTool import FlatCAMTool
  9. from flatcamGUI.GUIElements import RadioSet, FCEntry
  10. from PyQt5 import QtGui, QtCore, QtWidgets
  11. import gettext
  12. import FlatCAMTranslation as fcTranslate
  13. import builtins
  14. fcTranslate.apply_language('strings')
  15. if '_' not in builtins.__dict__:
  16. _ = gettext.gettext
  17. class Film(FlatCAMTool):
  18. toolName = _("Film PCB")
  19. def __init__(self, app):
  20. FlatCAMTool.__init__(self, app)
  21. # Title
  22. title_label = QtWidgets.QLabel("%s" % self.toolName)
  23. title_label.setStyleSheet("""
  24. QLabel
  25. {
  26. font-size: 16px;
  27. font-weight: bold;
  28. }
  29. """)
  30. self.layout.addWidget(title_label)
  31. # Form Layout
  32. tf_form_layout = QtWidgets.QFormLayout()
  33. self.layout.addLayout(tf_form_layout)
  34. # Type of object for which to create the film
  35. self.tf_type_obj_combo = QtWidgets.QComboBox()
  36. self.tf_type_obj_combo.addItem("Gerber")
  37. self.tf_type_obj_combo.addItem("Excellon")
  38. self.tf_type_obj_combo.addItem("Geometry")
  39. # we get rid of item1 ("Excellon") as it is not suitable for creating film
  40. self.tf_type_obj_combo.view().setRowHidden(1, True)
  41. self.tf_type_obj_combo.setItemIcon(0, QtGui.QIcon("share/flatcam_icon16.png"))
  42. self.tf_type_obj_combo.setItemIcon(2, QtGui.QIcon("share/geometry16.png"))
  43. self.tf_type_obj_combo_label = QtWidgets.QLabel('%s:' % _("Object Type"))
  44. self.tf_type_obj_combo_label.setToolTip(
  45. _("Specify the type of object for which to create the film.\n"
  46. "The object can be of type: Gerber or Geometry.\n"
  47. "The selection here decide the type of objects that will be\n"
  48. "in the Film Object combobox.")
  49. )
  50. tf_form_layout.addRow(self.tf_type_obj_combo_label, self.tf_type_obj_combo)
  51. # List of objects for which we can create the film
  52. self.tf_object_combo = QtWidgets.QComboBox()
  53. self.tf_object_combo.setModel(self.app.collection)
  54. self.tf_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
  55. self.tf_object_combo.setCurrentIndex(1)
  56. self.tf_object_label = QtWidgets.QLabel('%s:' % _("Film Object"))
  57. self.tf_object_label.setToolTip(
  58. _("Object for which to create the film.")
  59. )
  60. tf_form_layout.addRow(self.tf_object_label, self.tf_object_combo)
  61. # Type of Box Object to be used as an envelope for film creation
  62. # Within this we can create negative
  63. self.tf_type_box_combo = QtWidgets.QComboBox()
  64. self.tf_type_box_combo.addItem("Gerber")
  65. self.tf_type_box_combo.addItem("Excellon")
  66. self.tf_type_box_combo.addItem("Geometry")
  67. # we get rid of item1 ("Excellon") as it is not suitable for box when creating film
  68. self.tf_type_box_combo.view().setRowHidden(1, True)
  69. self.tf_type_box_combo.setItemIcon(0, QtGui.QIcon("share/flatcam_icon16.png"))
  70. self.tf_type_box_combo.setItemIcon(2, QtGui.QIcon("share/geometry16.png"))
  71. self.tf_type_box_combo_label = QtWidgets.QLabel(_("Box Type:"))
  72. self.tf_type_box_combo_label.setToolTip(
  73. _("Specify the type of object to be used as an container for\n"
  74. "film creation. It can be: Gerber or Geometry type."
  75. "The selection here decide the type of objects that will be\n"
  76. "in the Box Object combobox.")
  77. )
  78. tf_form_layout.addRow(self.tf_type_box_combo_label, self.tf_type_box_combo)
  79. # Box
  80. self.tf_box_combo = QtWidgets.QComboBox()
  81. self.tf_box_combo.setModel(self.app.collection)
  82. self.tf_box_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
  83. self.tf_box_combo.setCurrentIndex(1)
  84. self.tf_box_combo_label = QtWidgets.QLabel('%s:' % _("Box Object"))
  85. self.tf_box_combo_label.setToolTip(
  86. _("The actual object that is used a container for the\n "
  87. "selected object for which we create the film.\n"
  88. "Usually it is the PCB outline but it can be also the\n"
  89. "same object for which the film is created.")
  90. )
  91. tf_form_layout.addRow(self.tf_box_combo_label, self.tf_box_combo)
  92. # Film Type
  93. self.film_type = RadioSet([{'label': _('Positive'), 'value': 'pos'},
  94. {'label': _('Negative'), 'value': 'neg'}])
  95. self.film_type_label = QtWidgets.QLabel(_("Film Type:"))
  96. self.film_type_label.setToolTip(
  97. _("Generate a Positive black film or a Negative film.\n"
  98. "Positive means that it will print the features\n"
  99. "with black on a white canvas.\n"
  100. "Negative means that it will print the features\n"
  101. "with white on a black canvas.\n"
  102. "The Film format is SVG.")
  103. )
  104. tf_form_layout.addRow(self.film_type_label, self.film_type)
  105. # Boundary for negative film generation
  106. self.boundary_entry = FCEntry()
  107. self.boundary_label = QtWidgets.QLabel('%s:' % _("Border"))
  108. self.boundary_label.setToolTip(
  109. _("Specify a border around the object.\n"
  110. "Only for negative film.\n"
  111. "It helps if we use as a Box Object the same \n"
  112. "object as in Film Object. It will create a thick\n"
  113. "black bar around the actual print allowing for a\n"
  114. "better delimitation of the outline features which are of\n"
  115. "white color like the rest and which may confound with the\n"
  116. "surroundings if not for this border.")
  117. )
  118. tf_form_layout.addRow(self.boundary_label, self.boundary_entry)
  119. self.film_scale_entry = FCEntry()
  120. self.film_scale_label = QtWidgets.QLabel('%s:' % _("Scale Stroke"))
  121. self.film_scale_label.setToolTip(
  122. _("Scale the line stroke thickness of each feature in the SVG file.\n"
  123. "It means that the line that envelope each SVG feature will be thicker or thinner,\n"
  124. "therefore the fine features may be more affected by this parameter.")
  125. )
  126. tf_form_layout.addRow(self.film_scale_label, self.film_scale_entry)
  127. # Buttons
  128. hlay = QtWidgets.QHBoxLayout()
  129. self.layout.addLayout(hlay)
  130. hlay.addStretch()
  131. self.film_object_button = QtWidgets.QPushButton(_("Save Film"))
  132. self.film_object_button.setToolTip(
  133. _("Create a Film for the selected object, within\n"
  134. "the specified box. Does not create a new \n "
  135. "FlatCAM object, but directly save it in SVG format\n"
  136. "which can be opened with Inkscape.")
  137. )
  138. hlay.addWidget(self.film_object_button)
  139. self.layout.addStretch()
  140. # ## Signals
  141. self.film_object_button.clicked.connect(self.on_film_creation)
  142. self.tf_type_obj_combo.currentIndexChanged.connect(self.on_type_obj_index_changed)
  143. self.tf_type_box_combo.currentIndexChanged.connect(self.on_type_box_index_changed)
  144. def on_type_obj_index_changed(self, index):
  145. obj_type = self.tf_type_obj_combo.currentIndex()
  146. self.tf_object_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
  147. self.tf_object_combo.setCurrentIndex(0)
  148. def on_type_box_index_changed(self, index):
  149. obj_type = self.tf_type_box_combo.currentIndex()
  150. self.tf_box_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
  151. self.tf_box_combo.setCurrentIndex(0)
  152. def run(self, toggle=True):
  153. self.app.report_usage("ToolFilm()")
  154. if toggle:
  155. # if the splitter is hidden, display it, else hide it but only if the current widget is the same
  156. if self.app.ui.splitter.sizes()[0] == 0:
  157. self.app.ui.splitter.setSizes([1, 1])
  158. else:
  159. try:
  160. if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
  161. # if tab is populated with the tool but it does not have the focus, focus on it
  162. if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
  163. # focus on Tool Tab
  164. self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
  165. else:
  166. self.app.ui.splitter.setSizes([0, 1])
  167. except AttributeError:
  168. pass
  169. else:
  170. if self.app.ui.splitter.sizes()[0] == 0:
  171. self.app.ui.splitter.setSizes([1, 1])
  172. FlatCAMTool.run(self)
  173. self.set_tool_ui()
  174. self.app.ui.notebook.setTabText(2, _("Film Tool"))
  175. def install(self, icon=None, separator=None, **kwargs):
  176. FlatCAMTool.install(self, icon, separator, shortcut='ALT+L', **kwargs)
  177. def set_tool_ui(self):
  178. self.reset_fields()
  179. f_type = self.app.defaults["tools_film_type"] if self.app.defaults["tools_film_type"] else 'neg'
  180. self.film_type.set_value(str(f_type))
  181. b_entry = self.app.defaults["tools_film_boundary"] if self.app.defaults["tools_film_boundary"] else 0.0
  182. self.boundary_entry.set_value(float(b_entry))
  183. scale_stroke_width = self.app.defaults["tools_film_scale"] if self.app.defaults["tools_film_scale"] else 0.0
  184. self.film_scale_entry.set_value(int(scale_stroke_width))
  185. def on_film_creation(self):
  186. try:
  187. name = self.tf_object_combo.currentText()
  188. except Exception as e:
  189. self.app.inform.emit('[ERROR_NOTCL] %s' %
  190. _("No FlatCAM object selected. Load an object for Film and retry."))
  191. return
  192. try:
  193. boxname = self.tf_box_combo.currentText()
  194. except Exception as e:
  195. self.app.inform.emit('[ERROR_NOTCL] %s' %
  196. _("No FlatCAM object selected. Load an object for Box and retry."))
  197. return
  198. try:
  199. border = float(self.boundary_entry.get_value())
  200. except ValueError:
  201. # try to convert comma to decimal point. if it's still not working error message and return
  202. try:
  203. border = float(self.boundary_entry.get_value().replace(',', '.'))
  204. except ValueError:
  205. self.app.inform.emit('[ERROR_NOTCL] %s' % _("Wrong value format entered, use a number."))
  206. return
  207. try:
  208. scale_stroke_width = int(self.film_scale_entry.get_value())
  209. except ValueError:
  210. self.app.inform.emit('[ERROR_NOTCL] %s' % _("Wrong value format entered, use a number."))
  211. return
  212. if border is None:
  213. border = 0
  214. self.app.inform.emit(_("Generating Film ..."))
  215. if self.film_type.get_value() == "pos":
  216. try:
  217. filename, _f = QtWidgets.QFileDialog.getSaveFileName(
  218. caption=_("Export SVG positive"),
  219. directory=self.app.get_last_save_folder() + '/' + name,
  220. filter="*.svg")
  221. except TypeError:
  222. filename, _f = QtWidgets.QFileDialog.getSaveFileName(caption=_("Export SVG positive"))
  223. filename = str(filename)
  224. if str(filename) == "":
  225. self.app.inform.emit('[WARNING_NOTCL] %s' % _("Export SVG positive cancelled."))
  226. return
  227. else:
  228. self.app.export_svg_positive(name, boxname, filename, scale_factor=scale_stroke_width)
  229. else:
  230. try:
  231. filename, _f = QtWidgets.QFileDialog.getSaveFileName(
  232. caption=_("Export SVG negative"),
  233. directory=self.app.get_last_save_folder() + '/' + name,
  234. filter="*.svg")
  235. except TypeError:
  236. filename, _f = QtWidgets.QFileDialog.getSaveFileName(caption=_("Export SVG negative"))
  237. filename = str(filename)
  238. if str(filename) == "":
  239. self.app.inform.emit('[WARNING_NOTCL] %s' % _("Export SVG negative cancelled."))
  240. return
  241. else:
  242. self.app.export_svg_negative(name, boxname, filename, border, scale_factor=scale_stroke_width)
  243. def reset_fields(self):
  244. self.tf_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
  245. self.tf_box_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))