ToolTransform.py 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907
  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 FlatCAMObj import *
  10. import gettext
  11. import FlatCAMTranslation as fcTranslate
  12. import builtins
  13. fcTranslate.apply_language('strings')
  14. if '_' not in builtins.__dict__:
  15. _ = gettext.gettext
  16. class ToolTransform(FlatCAMTool):
  17. toolName = _("Object Transform")
  18. rotateName = _("Rotate")
  19. skewName = _("Skew/Shear")
  20. scaleName = _("Scale")
  21. flipName = _("Mirror (Flip)")
  22. offsetName = _("Offset")
  23. def __init__(self, app):
  24. FlatCAMTool.__init__(self, app)
  25. self.transform_lay = QtWidgets.QVBoxLayout()
  26. self.layout.addLayout(self.transform_lay)
  27. # ## Title
  28. title_label = QtWidgets.QLabel("%s" % self.toolName)
  29. title_label.setStyleSheet("""
  30. QLabel
  31. {
  32. font-size: 16px;
  33. font-weight: bold;
  34. }
  35. """)
  36. self.transform_lay.addWidget(title_label)
  37. self.empty_label = QtWidgets.QLabel("")
  38. self.empty_label.setMinimumWidth(70)
  39. self.empty_label1 = QtWidgets.QLabel("")
  40. self.empty_label1.setMinimumWidth(70)
  41. self.empty_label2 = QtWidgets.QLabel("")
  42. self.empty_label2.setMinimumWidth(70)
  43. self.empty_label3 = QtWidgets.QLabel("")
  44. self.empty_label3.setMinimumWidth(70)
  45. self.empty_label4 = QtWidgets.QLabel("")
  46. self.empty_label4.setMinimumWidth(70)
  47. self.transform_lay.addWidget(self.empty_label)
  48. # ## Rotate Title
  49. rotate_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.rotateName)
  50. self.transform_lay.addWidget(rotate_title_label)
  51. # ## Layout
  52. form_layout = QtWidgets.QFormLayout()
  53. self.transform_lay.addLayout(form_layout)
  54. form_child = QtWidgets.QHBoxLayout()
  55. self.rotate_label = QtWidgets.QLabel('%s:' % _("Angle"))
  56. self.rotate_label.setToolTip(
  57. _("Angle for Rotation action, in degrees.\n"
  58. "Float number between -360 and 359.\n"
  59. "Positive numbers for CW motion.\n"
  60. "Negative numbers for CCW motion.")
  61. )
  62. self.rotate_label.setMinimumWidth(70)
  63. self.rotate_entry = FCEntry()
  64. # self.rotate_entry.setFixedWidth(70)
  65. self.rotate_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
  66. self.rotate_button = FCButton()
  67. self.rotate_button.set_value(_("Rotate"))
  68. self.rotate_button.setToolTip(
  69. _("Rotate the selected object(s).\n"
  70. "The point of reference is the middle of\n"
  71. "the bounding box for all selected objects.")
  72. )
  73. self.rotate_button.setMinimumWidth(90)
  74. form_child.addWidget(self.rotate_entry)
  75. form_child.addWidget(self.rotate_button)
  76. form_layout.addRow(self.rotate_label, form_child)
  77. self.transform_lay.addWidget(self.empty_label1)
  78. # ## Skew Title
  79. skew_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.skewName)
  80. self.transform_lay.addWidget(skew_title_label)
  81. # ## Form Layout
  82. form1_layout = QtWidgets.QFormLayout()
  83. self.transform_lay.addLayout(form1_layout)
  84. form1_child_1 = QtWidgets.QHBoxLayout()
  85. form1_child_2 = QtWidgets.QHBoxLayout()
  86. self.skewx_label = QtWidgets.QLabel('%s:' % _("Skew_X angle"))
  87. self.skewx_label.setToolTip(
  88. _("Angle for Skew action, in degrees.\n"
  89. "Float number between -360 and 359.")
  90. )
  91. self.skewx_label.setMinimumWidth(70)
  92. self.skewx_entry = FCEntry()
  93. self.skewx_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
  94. # self.skewx_entry.setFixedWidth(70)
  95. self.skewx_button = FCButton()
  96. self.skewx_button.set_value(_("Skew X"))
  97. self.skewx_button.setToolTip(
  98. _("Skew/shear the selected object(s).\n"
  99. "The point of reference is the middle of\n"
  100. "the bounding box for all selected objects."))
  101. self.skewx_button.setMinimumWidth(90)
  102. self.skewy_label = QtWidgets.QLabel('%s:' % _("Skew_Y angle"))
  103. self.skewy_label.setToolTip(
  104. _("Angle for Skew action, in degrees.\n"
  105. "Float number between -360 and 359.")
  106. )
  107. self.skewy_label.setMinimumWidth(70)
  108. self.skewy_entry = FCEntry()
  109. self.skewy_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
  110. # self.skewy_entry.setFixedWidth(70)
  111. self.skewy_button = FCButton()
  112. self.skewy_button.set_value(_("Skew Y"))
  113. self.skewy_button.setToolTip(
  114. _("Skew/shear the selected object(s).\n"
  115. "The point of reference is the middle of\n"
  116. "the bounding box for all selected objects."))
  117. self.skewy_button.setMinimumWidth(90)
  118. form1_child_1.addWidget(self.skewx_entry)
  119. form1_child_1.addWidget(self.skewx_button)
  120. form1_child_2.addWidget(self.skewy_entry)
  121. form1_child_2.addWidget(self.skewy_button)
  122. form1_layout.addRow(self.skewx_label, form1_child_1)
  123. form1_layout.addRow(self.skewy_label, form1_child_2)
  124. self.transform_lay.addWidget(self.empty_label2)
  125. # ## Scale Title
  126. scale_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.scaleName)
  127. self.transform_lay.addWidget(scale_title_label)
  128. # ## Form Layout
  129. form2_layout = QtWidgets.QFormLayout()
  130. self.transform_lay.addLayout(form2_layout)
  131. form2_child_1 = QtWidgets.QHBoxLayout()
  132. form2_child_2 = QtWidgets.QHBoxLayout()
  133. self.scalex_label = QtWidgets.QLabel('%s:' % _("Scale_X factor"))
  134. self.scalex_label.setToolTip(
  135. _("Factor for scaling on X axis.")
  136. )
  137. self.scalex_label.setMinimumWidth(70)
  138. self.scalex_entry = FCEntry()
  139. self.scalex_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
  140. # self.scalex_entry.setFixedWidth(70)
  141. self.scalex_button = FCButton()
  142. self.scalex_button.set_value(_("Scale X"))
  143. self.scalex_button.setToolTip(
  144. _("Scale the selected object(s).\n"
  145. "The point of reference depends on \n"
  146. "the Scale reference checkbox state."))
  147. self.scalex_button.setMinimumWidth(90)
  148. self.scaley_label = QtWidgets.QLabel('%s:' % _("Scale_Y factor"))
  149. self.scaley_label.setToolTip(
  150. _("Factor for scaling on Y axis.")
  151. )
  152. self.scaley_label.setMinimumWidth(70)
  153. self.scaley_entry = FCEntry()
  154. self.scaley_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
  155. # self.scaley_entry.setFixedWidth(70)
  156. self.scaley_button = FCButton()
  157. self.scaley_button.set_value(_("Scale Y"))
  158. self.scaley_button.setToolTip(
  159. _("Scale the selected object(s).\n"
  160. "The point of reference depends on \n"
  161. "the Scale reference checkbox state."))
  162. self.scaley_button.setMinimumWidth(90)
  163. self.scale_link_cb = FCCheckBox()
  164. self.scale_link_cb.set_value(True)
  165. self.scale_link_cb.setText(_("Link"))
  166. self.scale_link_cb.setToolTip(
  167. _("Scale the selected object(s)\n"
  168. "using the Scale_X factor for both axis.")
  169. )
  170. self.scale_link_cb.setMinimumWidth(70)
  171. self.scale_zero_ref_cb = FCCheckBox()
  172. self.scale_zero_ref_cb.set_value(True)
  173. self.scale_zero_ref_cb.setText('%s' % _("Scale Reference"))
  174. self.scale_zero_ref_cb.setToolTip(
  175. _("Scale the selected object(s)\n"
  176. "using the origin reference when checked,\n"
  177. "and the center of the biggest bounding box\n"
  178. "of the selected objects when unchecked."))
  179. form2_child_1.addWidget(self.scalex_entry)
  180. form2_child_1.addWidget(self.scalex_button)
  181. form2_child_2.addWidget(self.scaley_entry)
  182. form2_child_2.addWidget(self.scaley_button)
  183. form2_layout.addRow(self.scalex_label, form2_child_1)
  184. form2_layout.addRow(self.scaley_label, form2_child_2)
  185. form2_layout.addRow(self.scale_link_cb, self.scale_zero_ref_cb)
  186. self.ois_scale = OptionalInputSection(self.scale_link_cb, [self.scaley_entry, self.scaley_button], logic=False)
  187. self.transform_lay.addWidget(self.empty_label3)
  188. # ## Offset Title
  189. offset_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.offsetName)
  190. self.transform_lay.addWidget(offset_title_label)
  191. # ## Form Layout
  192. form3_layout = QtWidgets.QFormLayout()
  193. self.transform_lay.addLayout(form3_layout)
  194. form3_child_1 = QtWidgets.QHBoxLayout()
  195. form3_child_2 = QtWidgets.QHBoxLayout()
  196. self.offx_label = QtWidgets.QLabel('%s:' % _("Offset_X val"))
  197. self.offx_label.setToolTip(
  198. _("Distance to offset on X axis. In current units.")
  199. )
  200. self.offx_label.setMinimumWidth(70)
  201. self.offx_entry = FCEntry()
  202. self.offx_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
  203. # self.offx_entry.setFixedWidth(70)
  204. self.offx_button = FCButton()
  205. self.offx_button.set_value(_("Offset X"))
  206. self.offx_button.setToolTip(
  207. _("Offset the selected object(s).\n"
  208. "The point of reference is the middle of\n"
  209. "the bounding box for all selected objects.\n"))
  210. self.offx_button.setMinimumWidth(90)
  211. self.offy_label = QtWidgets.QLabel('%s:' % _("Offset_Y val"))
  212. self.offy_label.setToolTip(
  213. _("Distance to offset on Y axis. In current units.")
  214. )
  215. self.offy_label.setMinimumWidth(70)
  216. self.offy_entry = FCEntry()
  217. self.offy_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
  218. # self.offy_entry.setFixedWidth(70)
  219. self.offy_button = FCButton()
  220. self.offy_button.set_value(_("Offset Y"))
  221. self.offy_button.setToolTip(
  222. _("Offset the selected object(s).\n"
  223. "The point of reference is the middle of\n"
  224. "the bounding box for all selected objects.\n"))
  225. self.offy_button.setMinimumWidth(90)
  226. form3_child_1.addWidget(self.offx_entry)
  227. form3_child_1.addWidget(self.offx_button)
  228. form3_child_2.addWidget(self.offy_entry)
  229. form3_child_2.addWidget(self.offy_button)
  230. form3_layout.addRow(self.offx_label, form3_child_1)
  231. form3_layout.addRow(self.offy_label, form3_child_2)
  232. self.transform_lay.addWidget(self.empty_label4)
  233. # ## Flip Title
  234. flip_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.flipName)
  235. self.transform_lay.addWidget(flip_title_label)
  236. # ## Form Layout
  237. form4_layout = QtWidgets.QFormLayout()
  238. form4_child_hlay = QtWidgets.QHBoxLayout()
  239. self.transform_lay.addLayout(form4_child_hlay)
  240. self.transform_lay.addLayout(form4_layout)
  241. form4_child_1 = QtWidgets.QHBoxLayout()
  242. self.flipx_button = FCButton()
  243. self.flipx_button.set_value(_("Flip on X"))
  244. self.flipx_button.setToolTip(
  245. _("Flip the selected object(s) over the X axis.\n"
  246. "Does not create a new object.\n ")
  247. )
  248. self.flipx_button.setMinimumWidth(100)
  249. self.flipy_button = FCButton()
  250. self.flipy_button.set_value(_("Flip on Y"))
  251. self.flipy_button.setToolTip(
  252. _("Flip the selected object(s) over the X axis.\n"
  253. "Does not create a new object.\n ")
  254. )
  255. self.flipy_button.setMinimumWidth(90)
  256. self.flip_ref_cb = FCCheckBox()
  257. self.flip_ref_cb.set_value(True)
  258. self.flip_ref_cb.setText('%s' % _("Mirror Reference"))
  259. self.flip_ref_cb.setToolTip(
  260. _("Flip the selected object(s)\n"
  261. "around the point in Point Entry Field.\n"
  262. "\n"
  263. "The point coordinates can be captured by\n"
  264. "left click on canvas together with pressing\n"
  265. "SHIFT key. \n"
  266. "Then click Add button to insert coordinates.\n"
  267. "Or enter the coords in format (x, y) in the\n"
  268. "Point Entry field and click Flip on X(Y)"))
  269. self.flip_ref_cb.setMinimumWidth(70)
  270. self.flip_ref_label = QtWidgets.QLabel('%s:' % _(" Mirror Ref. Point"))
  271. self.flip_ref_label.setToolTip(
  272. _("Coordinates in format (x, y) used as reference for mirroring.\n"
  273. "The 'x' in (x, y) will be used when using Flip on X and\n"
  274. "the 'y' in (x, y) will be used when using Flip on Y and")
  275. )
  276. self.flip_ref_label.setMinimumWidth(70)
  277. self.flip_ref_entry = EvalEntry2("(0, 0)")
  278. self.flip_ref_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
  279. # self.flip_ref_entry.setFixedWidth(70)
  280. self.flip_ref_button = FCButton()
  281. self.flip_ref_button.set_value(_("Add"))
  282. self.flip_ref_button.setToolTip(
  283. _("The point coordinates can be captured by\n"
  284. "left click on canvas together with pressing\n"
  285. "SHIFT key. Then click Add button to insert."))
  286. self.flip_ref_button.setMinimumWidth(90)
  287. form4_child_hlay.addStretch()
  288. form4_child_hlay.addWidget(self.flipx_button)
  289. form4_child_hlay.addWidget(self.flipy_button)
  290. form4_child_1.addWidget(self.flip_ref_entry)
  291. form4_child_1.addWidget(self.flip_ref_button)
  292. form4_layout.addRow(self.flip_ref_cb)
  293. form4_layout.addRow(self.flip_ref_label, form4_child_1)
  294. self.ois_flip = OptionalInputSection(self.flip_ref_cb, [self.flip_ref_entry, self.flip_ref_button], logic=True)
  295. self.transform_lay.addStretch()
  296. # ## Signals
  297. self.rotate_button.clicked.connect(self.on_rotate)
  298. self.skewx_button.clicked.connect(self.on_skewx)
  299. self.skewy_button.clicked.connect(self.on_skewy)
  300. self.scalex_button.clicked.connect(self.on_scalex)
  301. self.scaley_button.clicked.connect(self.on_scaley)
  302. self.offx_button.clicked.connect(self.on_offx)
  303. self.offy_button.clicked.connect(self.on_offy)
  304. self.flipx_button.clicked.connect(self.on_flipx)
  305. self.flipy_button.clicked.connect(self.on_flipy)
  306. self.flip_ref_button.clicked.connect(self.on_flip_add_coords)
  307. self.rotate_entry.returnPressed.connect(self.on_rotate)
  308. self.skewx_entry.returnPressed.connect(self.on_skewx)
  309. self.skewy_entry.returnPressed.connect(self.on_skewy)
  310. self.scalex_entry.returnPressed.connect(self.on_scalex)
  311. self.scaley_entry.returnPressed.connect(self.on_scaley)
  312. self.offx_entry.returnPressed.connect(self.on_offx)
  313. self.offy_entry.returnPressed.connect(self.on_offy)
  314. def run(self, toggle=True):
  315. self.app.report_usage("ToolTransform()")
  316. if toggle:
  317. # if the splitter is hidden, display it, else hide it but only if the current widget is the same
  318. if self.app.ui.splitter.sizes()[0] == 0:
  319. self.app.ui.splitter.setSizes([1, 1])
  320. else:
  321. try:
  322. if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
  323. # if tab is populated with the tool but it does not have the focus, focus on it
  324. if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
  325. # focus on Tool Tab
  326. self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
  327. else:
  328. self.app.ui.splitter.setSizes([0, 1])
  329. except AttributeError:
  330. pass
  331. else:
  332. if self.app.ui.splitter.sizes()[0] == 0:
  333. self.app.ui.splitter.setSizes([1, 1])
  334. FlatCAMTool.run(self)
  335. self.set_tool_ui()
  336. self.app.ui.notebook.setTabText(2, _("Transform Tool"))
  337. def install(self, icon=None, separator=None, **kwargs):
  338. FlatCAMTool.install(self, icon, separator, shortcut='ALT+E', **kwargs)
  339. def set_tool_ui(self):
  340. # ## Initialize form
  341. if self.app.defaults["tools_transform_rotate"]:
  342. self.rotate_entry.set_value(self.app.defaults["tools_transform_rotate"])
  343. else:
  344. self.rotate_entry.set_value(0.0)
  345. if self.app.defaults["tools_transform_skew_x"]:
  346. self.skewx_entry.set_value(self.app.defaults["tools_transform_skew_x"])
  347. else:
  348. self.skewx_entry.set_value(0.0)
  349. if self.app.defaults["tools_transform_skew_y"]:
  350. self.skewy_entry.set_value(self.app.defaults["tools_transform_skew_y"])
  351. else:
  352. self.skewy_entry.set_value(0.0)
  353. if self.app.defaults["tools_transform_scale_x"]:
  354. self.scalex_entry.set_value(self.app.defaults["tools_transform_scale_x"])
  355. else:
  356. self.scalex_entry.set_value(1.0)
  357. if self.app.defaults["tools_transform_scale_y"]:
  358. self.scaley_entry.set_value(self.app.defaults["tools_transform_scale_y"])
  359. else:
  360. self.scaley_entry.set_value(1.0)
  361. if self.app.defaults["tools_transform_scale_link"]:
  362. self.scale_link_cb.set_value(self.app.defaults["tools_transform_scale_link"])
  363. else:
  364. self.scale_link_cb.set_value(True)
  365. if self.app.defaults["tools_transform_scale_reference"]:
  366. self.scale_zero_ref_cb.set_value(self.app.defaults["tools_transform_scale_reference"])
  367. else:
  368. self.scale_zero_ref_cb.set_value(True)
  369. if self.app.defaults["tools_transform_offset_x"]:
  370. self.offx_entry.set_value(self.app.defaults["tools_transform_offset_x"])
  371. else:
  372. self.offx_entry.set_value(0.0)
  373. if self.app.defaults["tools_transform_offset_y"]:
  374. self.offy_entry.set_value(self.app.defaults["tools_transform_offset_y"])
  375. else:
  376. self.offy_entry.set_value(0.0)
  377. if self.app.defaults["tools_transform_mirror_reference"]:
  378. self.flip_ref_cb.set_value(self.app.defaults["tools_transform_mirror_reference"])
  379. else:
  380. self.flip_ref_cb.set_value(False)
  381. if self.app.defaults["tools_transform_mirror_point"]:
  382. self.flip_ref_entry.set_value(self.app.defaults["tools_transform_mirror_point"])
  383. else:
  384. self.flip_ref_entry.set_value((0, 0))
  385. def on_rotate(self):
  386. try:
  387. value = float(self.rotate_entry.get_value())
  388. except ValueError:
  389. # try to convert comma to decimal point. if it's still not working error message and return
  390. try:
  391. value = float(self.rotate_entry.get_value().replace(',', '.'))
  392. except ValueError:
  393. self.app.inform.emit('[ERROR_NOTCL] %s' %
  394. _("Wrong value format entered, use a number."))
  395. return
  396. self.app.worker_task.emit({'fcn': self.on_rotate_action,
  397. 'params': [value]})
  398. # self.on_rotate_action(value)
  399. return
  400. def on_flipx(self):
  401. # self.on_flip("Y")
  402. axis = 'Y'
  403. self.app.worker_task.emit({'fcn': self.on_flip,
  404. 'params': [axis]})
  405. return
  406. def on_flipy(self):
  407. # self.on_flip("X")
  408. axis = 'X'
  409. self.app.worker_task.emit({'fcn': self.on_flip,
  410. 'params': [axis]})
  411. return
  412. def on_flip_add_coords(self):
  413. val = self.app.clipboard.text()
  414. self.flip_ref_entry.set_value(val)
  415. def on_skewx(self):
  416. try:
  417. value = float(self.skewx_entry.get_value())
  418. except ValueError:
  419. # try to convert comma to decimal point. if it's still not working error message and return
  420. try:
  421. value = float(self.skewx_entry.get_value().replace(',', '.'))
  422. except ValueError:
  423. self.app.inform.emit('[ERROR_NOTCL] %s' %
  424. _("Wrong value format entered, use a number."))
  425. return
  426. # self.on_skew("X", value)
  427. axis = 'X'
  428. self.app.worker_task.emit({'fcn': self.on_skew,
  429. 'params': [axis, value]})
  430. return
  431. def on_skewy(self):
  432. try:
  433. value = float(self.skewy_entry.get_value())
  434. except ValueError:
  435. # try to convert comma to decimal point. if it's still not working error message and return
  436. try:
  437. value = float(self.skewy_entry.get_value().replace(',', '.'))
  438. except ValueError:
  439. self.app.inform.emit('[ERROR_NOTCL] %s' %
  440. _("Wrong value format entered, use a number."))
  441. return
  442. # self.on_skew("Y", value)
  443. axis = 'Y'
  444. self.app.worker_task.emit({'fcn': self.on_skew,
  445. 'params': [axis, value]})
  446. return
  447. def on_scalex(self):
  448. try:
  449. xvalue = float(self.scalex_entry.get_value())
  450. except ValueError:
  451. # try to convert comma to decimal point. if it's still not working error message and return
  452. try:
  453. xvalue = float(self.scalex_entry.get_value().replace(',', '.'))
  454. except ValueError:
  455. self.app.inform.emit('[ERROR_NOTCL] %s' %
  456. _("Wrong value format entered, use a number."))
  457. return
  458. # scaling to zero has no sense so we remove it, because scaling with 1 does nothing
  459. if xvalue == 0:
  460. xvalue = 1
  461. if self.scale_link_cb.get_value():
  462. yvalue = xvalue
  463. else:
  464. yvalue = 1
  465. axis = 'X'
  466. point = (0, 0)
  467. if self.scale_zero_ref_cb.get_value():
  468. self.app.worker_task.emit({'fcn': self.on_scale,
  469. 'params': [axis, xvalue, yvalue, point]})
  470. # self.on_scale("X", xvalue, yvalue, point=(0,0))
  471. else:
  472. # self.on_scale("X", xvalue, yvalue)
  473. self.app.worker_task.emit({'fcn': self.on_scale,
  474. 'params': [axis, xvalue, yvalue]})
  475. return
  476. def on_scaley(self):
  477. xvalue = 1
  478. try:
  479. yvalue = float(self.scaley_entry.get_value())
  480. except ValueError:
  481. # try to convert comma to decimal point. if it's still not working error message and return
  482. try:
  483. yvalue = float(self.scaley_entry.get_value().replace(',', '.'))
  484. except ValueError:
  485. self.app.inform.emit('[ERROR_NOTCL] %s' %
  486. _("Wrong value format entered, use a number."))
  487. return
  488. # scaling to zero has no sense so we remove it, because scaling with 1 does nothing
  489. if yvalue == 0:
  490. yvalue = 1
  491. axis = 'Y'
  492. point = (0, 0)
  493. if self.scale_zero_ref_cb.get_value():
  494. self.app.worker_task.emit({'fcn': self.on_scale,
  495. 'params': [axis, xvalue, yvalue, point]})
  496. # self.on_scale("Y", xvalue, yvalue, point=(0,0))
  497. else:
  498. # self.on_scale("Y", xvalue, yvalue)
  499. self.app.worker_task.emit({'fcn': self.on_scale,
  500. 'params': [axis, xvalue, yvalue]})
  501. return
  502. def on_offx(self):
  503. try:
  504. value = float(self.offx_entry.get_value())
  505. except ValueError:
  506. # try to convert comma to decimal point. if it's still not working error message and return
  507. try:
  508. value = float(self.offx_entry.get_value().replace(',', '.'))
  509. except ValueError:
  510. self.app.inform.emit('[ERROR_NOTCL] %s' %
  511. _("Wrong value format entered, use a number."))
  512. return
  513. # self.on_offset("X", value)
  514. axis = 'X'
  515. self.app.worker_task.emit({'fcn': self.on_offset,
  516. 'params': [axis, value]})
  517. return
  518. def on_offy(self):
  519. try:
  520. value = float(self.offy_entry.get_value())
  521. except ValueError:
  522. # try to convert comma to decimal point. if it's still not working error message and return
  523. try:
  524. value = float(self.offy_entry.get_value().replace(',', '.'))
  525. except ValueError:
  526. self.app.inform.emit('[ERROR_NOTCL] %s' %
  527. _("Wrong value format entered, use a number."))
  528. return
  529. # self.on_offset("Y", value)
  530. axis = 'Y'
  531. self.app.worker_task.emit({'fcn': self.on_offset,
  532. 'params': [axis, value]})
  533. return
  534. def on_rotate_action(self, num):
  535. obj_list = self.app.collection.get_selected()
  536. xminlist = []
  537. yminlist = []
  538. xmaxlist = []
  539. ymaxlist = []
  540. if not obj_list:
  541. self.app.inform.emit('[WARNING_NOTCL] %s' %
  542. _("No object selected. Please Select an object to rotate!"))
  543. return
  544. else:
  545. with self.app.proc_container.new(_("Appying Rotate")):
  546. try:
  547. # first get a bounding box to fit all
  548. for obj in obj_list:
  549. if isinstance(obj, FlatCAMCNCjob):
  550. pass
  551. else:
  552. xmin, ymin, xmax, ymax = obj.bounds()
  553. xminlist.append(xmin)
  554. yminlist.append(ymin)
  555. xmaxlist.append(xmax)
  556. ymaxlist.append(ymax)
  557. # get the minimum x,y and maximum x,y for all objects selected
  558. xminimal = min(xminlist)
  559. yminimal = min(yminlist)
  560. xmaximal = max(xmaxlist)
  561. ymaximal = max(ymaxlist)
  562. self.app.progress.emit(20)
  563. px = 0.5 * (xminimal + xmaximal)
  564. py = 0.5 * (yminimal + ymaximal)
  565. for sel_obj in obj_list:
  566. if isinstance(sel_obj, FlatCAMCNCjob):
  567. self.app.inform.emit(_("CNCJob objects can't be rotated."))
  568. else:
  569. sel_obj.rotate(-num, point=(px, py))
  570. self.app.object_changed.emit(sel_obj)
  571. # add information to the object that it was changed and how much
  572. sel_obj.options['rotate'] = num
  573. sel_obj.plot()
  574. self.app.inform.emit('[success] %s...' % _('Rotate done'))
  575. self.app.progress.emit(100)
  576. except Exception as e:
  577. self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
  578. (_("Due of"), str(e), _("action was not executed.")))
  579. return
  580. def on_flip(self, axis):
  581. obj_list = self.app.collection.get_selected()
  582. xminlist = []
  583. yminlist = []
  584. xmaxlist = []
  585. ymaxlist = []
  586. if not obj_list:
  587. self.app.inform.emit('[WARNING_NOTCL] %s!' %
  588. _("No object selected. Please Select an object to flip"))
  589. return
  590. else:
  591. with self.app.proc_container.new(_("Applying Flip")):
  592. try:
  593. # get mirroring coords from the point entry
  594. if self.flip_ref_cb.isChecked():
  595. px, py = eval('{}'.format(self.flip_ref_entry.text()))
  596. # get mirroing coords from the center of an all-enclosing bounding box
  597. else:
  598. # first get a bounding box to fit all
  599. for obj in obj_list:
  600. if isinstance(obj, FlatCAMCNCjob):
  601. pass
  602. else:
  603. xmin, ymin, xmax, ymax = obj.bounds()
  604. xminlist.append(xmin)
  605. yminlist.append(ymin)
  606. xmaxlist.append(xmax)
  607. ymaxlist.append(ymax)
  608. # get the minimum x,y and maximum x,y for all objects selected
  609. xminimal = min(xminlist)
  610. yminimal = min(yminlist)
  611. xmaximal = max(xmaxlist)
  612. ymaximal = max(ymaxlist)
  613. px = 0.5 * (xminimal + xmaximal)
  614. py = 0.5 * (yminimal + ymaximal)
  615. self.app.progress.emit(20)
  616. # execute mirroring
  617. for sel_obj in obj_list:
  618. if isinstance(sel_obj, FlatCAMCNCjob):
  619. self.app.inform.emit(_("CNCJob objects can't be mirrored/flipped."))
  620. else:
  621. if axis is 'X':
  622. sel_obj.mirror('X', (px, py))
  623. # add information to the object that it was changed and how much
  624. # the axis is reversed because of the reference
  625. if 'mirror_y' in sel_obj.options:
  626. sel_obj.options['mirror_y'] = not sel_obj.options['mirror_y']
  627. else:
  628. sel_obj.options['mirror_y'] = True
  629. self.app.inform.emit('[success] %s...' %
  630. _('Flip on the Y axis done'))
  631. elif axis is 'Y':
  632. sel_obj.mirror('Y', (px, py))
  633. # add information to the object that it was changed and how much
  634. # the axis is reversed because of the reference
  635. if 'mirror_x' in sel_obj.options:
  636. sel_obj.options['mirror_x'] = not sel_obj.options['mirror_x']
  637. else:
  638. sel_obj.options['mirror_x'] = True
  639. self.app.inform.emit('[success] %s...' %
  640. _('Flip on the X axis done'))
  641. self.app.object_changed.emit(sel_obj)
  642. sel_obj.plot()
  643. self.app.progress.emit(100)
  644. except Exception as e:
  645. self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
  646. (_("Due of"), str(e), _("action was not executed.")))
  647. return
  648. def on_skew(self, axis, num):
  649. obj_list = self.app.collection.get_selected()
  650. xminlist = []
  651. yminlist = []
  652. if not obj_list:
  653. self.app.inform.emit('[WARNING_NOTCL] %s' %
  654. _("No object selected. Please Select an object to shear/skew!"))
  655. return
  656. else:
  657. with self.app.proc_container.new(_("Applying Skew")):
  658. try:
  659. # first get a bounding box to fit all
  660. for obj in obj_list:
  661. if isinstance(obj, FlatCAMCNCjob):
  662. pass
  663. else:
  664. xmin, ymin, xmax, ymax = obj.bounds()
  665. xminlist.append(xmin)
  666. yminlist.append(ymin)
  667. # get the minimum x,y and maximum x,y for all objects selected
  668. xminimal = min(xminlist)
  669. yminimal = min(yminlist)
  670. self.app.progress.emit(20)
  671. for sel_obj in obj_list:
  672. if isinstance(sel_obj, FlatCAMCNCjob):
  673. self.app.inform.emit(_("CNCJob objects can't be skewed."))
  674. else:
  675. if axis is 'X':
  676. sel_obj.skew(num, 0, point=(xminimal, yminimal))
  677. # add information to the object that it was changed and how much
  678. sel_obj.options['skew_x'] = num
  679. elif axis is 'Y':
  680. sel_obj.skew(0, num, point=(xminimal, yminimal))
  681. # add information to the object that it was changed and how much
  682. sel_obj.options['skew_y'] = num
  683. self.app.object_changed.emit(sel_obj)
  684. sel_obj.plot()
  685. self.app.inform.emit('[success] %s %s %s...' %
  686. (_('Skew on the'), str(axis), _("axis done")))
  687. self.app.progress.emit(100)
  688. except Exception as e:
  689. self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
  690. (_("Due of"), str(e), _("action was not executed.")))
  691. return
  692. def on_scale(self, axis, xfactor, yfactor, point=None):
  693. obj_list = self.app.collection.get_selected()
  694. xminlist = []
  695. yminlist = []
  696. xmaxlist = []
  697. ymaxlist = []
  698. if not obj_list:
  699. self.app.inform.emit('[WARNING_NOTCL] %s' %
  700. _("No object selected. Please Select an object to scale!"))
  701. return
  702. else:
  703. with self.app.proc_container.new(_("Applying Scale")):
  704. try:
  705. # first get a bounding box to fit all
  706. for obj in obj_list:
  707. if isinstance(obj, FlatCAMCNCjob):
  708. pass
  709. else:
  710. xmin, ymin, xmax, ymax = obj.bounds()
  711. xminlist.append(xmin)
  712. yminlist.append(ymin)
  713. xmaxlist.append(xmax)
  714. ymaxlist.append(ymax)
  715. # get the minimum x,y and maximum x,y for all objects selected
  716. xminimal = min(xminlist)
  717. yminimal = min(yminlist)
  718. xmaximal = max(xmaxlist)
  719. ymaximal = max(ymaxlist)
  720. self.app.progress.emit(20)
  721. if point is None:
  722. px = 0.5 * (xminimal + xmaximal)
  723. py = 0.5 * (yminimal + ymaximal)
  724. else:
  725. px = 0
  726. py = 0
  727. for sel_obj in obj_list:
  728. if isinstance(sel_obj, FlatCAMCNCjob):
  729. self.app.inform.emit(_("CNCJob objects can't be scaled."))
  730. else:
  731. sel_obj.scale(xfactor, yfactor, point=(px, py))
  732. # add information to the object that it was changed and how much
  733. sel_obj.options['scale_x'] = xfactor
  734. sel_obj.options['scale_y'] = yfactor
  735. self.app.object_changed.emit(sel_obj)
  736. sel_obj.plot()
  737. self.app.inform.emit('[success] %s %s %s...' %
  738. (_('Scale on the'), str(axis), _('axis done')))
  739. self.app.progress.emit(100)
  740. except Exception as e:
  741. self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
  742. (_("Due of"), str(e), _("action was not executed.")))
  743. return
  744. def on_offset(self, axis, num):
  745. obj_list = self.app.collection.get_selected()
  746. if not obj_list:
  747. self.app.inform.emit('[WARNING_NOTCL] %s' %
  748. _("No object selected. Please Select an object to offset!"))
  749. return
  750. else:
  751. with self.app.proc_container.new(_("Applying Offset")):
  752. try:
  753. self.app.progress.emit(20)
  754. for sel_obj in obj_list:
  755. if isinstance(sel_obj, FlatCAMCNCjob):
  756. self.app.inform.emit(_("CNCJob objects can't be offset."))
  757. else:
  758. if axis is 'X':
  759. sel_obj.offset((num, 0))
  760. # add information to the object that it was changed and how much
  761. sel_obj.options['offset_x'] = num
  762. elif axis is 'Y':
  763. sel_obj.offset((0, num))
  764. # add information to the object that it was changed and how much
  765. sel_obj.options['offset_y'] = num
  766. self.app.object_changed.emit(sel_obj)
  767. sel_obj.plot()
  768. self.app.inform.emit('[success] %s %s %s...' %
  769. (_('Offset on the'), str(axis), _('axis done')))
  770. self.app.progress.emit(100)
  771. except Exception as e:
  772. self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
  773. (_("Due of"), str(e), _("action was not executed.")))
  774. return
  775. # end of file