ToolTransform.py 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897
  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 PyQt5 import QtGui, QtCore, QtWidgets
  9. from PyQt5.QtCore import Qt
  10. from GUIElements import FCEntry, FCButton, OptionalInputSection
  11. from FlatCAMTool import FlatCAMTool
  12. from FlatCAMObj import *
  13. import gettext
  14. import FlatCAMTranslation as fcTranslate
  15. fcTranslate.apply_language('ToolTransform')
  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.setFixedWidth(50)
  39. self.empty_label1 = QtWidgets.QLabel("")
  40. self.empty_label1.setFixedWidth(70)
  41. self.empty_label2 = QtWidgets.QLabel("")
  42. self.empty_label2.setFixedWidth(70)
  43. self.empty_label3 = QtWidgets.QLabel("")
  44. self.empty_label3.setFixedWidth(70)
  45. self.empty_label4 = QtWidgets.QLabel("")
  46. self.empty_label4.setFixedWidth(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(_("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.setFixedWidth(50)
  63. self.rotate_entry = FCEntry()
  64. # self.rotate_entry.setFixedWidth(60)
  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.setFixedWidth(60)
  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(_("Angle X:"))
  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.setFixedWidth(50)
  92. self.skewx_entry = FCEntry()
  93. self.skewx_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
  94. # self.skewx_entry.setFixedWidth(60)
  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.setFixedWidth(60)
  102. self.skewy_label = QtWidgets.QLabel(_("Angle Y:"))
  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.setFixedWidth(50)
  108. self.skewy_entry = FCEntry()
  109. self.skewy_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
  110. # self.skewy_entry.setFixedWidth(60)
  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.setFixedWidth(60)
  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(_("Factor X:"))
  134. self.scalex_label.setToolTip(
  135. _("Factor for Scale action over X axis.")
  136. )
  137. self.scalex_label.setFixedWidth(50)
  138. self.scalex_entry = FCEntry()
  139. self.scalex_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
  140. # self.scalex_entry.setFixedWidth(60)
  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.setFixedWidth(60)
  148. self.scaley_label = QtWidgets.QLabel(_("Factor Y:"))
  149. self.scaley_label.setToolTip(
  150. _("Factor for Scale action over Y axis.")
  151. )
  152. self.scaley_label.setFixedWidth(50)
  153. self.scaley_entry = FCEntry()
  154. self.scaley_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
  155. # self.scaley_entry.setFixedWidth(60)
  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.setFixedWidth(60)
  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 Factor X for both axis."))
  169. self.scale_link_cb.setFixedWidth(50)
  170. self.scale_zero_ref_cb = FCCheckBox()
  171. self.scale_zero_ref_cb.set_value(True)
  172. self.scale_zero_ref_cb.setText(_("Scale Reference"))
  173. self.scale_zero_ref_cb.setToolTip(
  174. _("Scale the selected object(s)\n"
  175. "using the origin reference when checked,\n"
  176. "and the center of the biggest bounding box\n"
  177. "of the selected objects when unchecked."))
  178. form2_child_1.addWidget(self.scalex_entry)
  179. form2_child_1.addWidget(self.scalex_button)
  180. form2_child_2.addWidget(self.scaley_entry)
  181. form2_child_2.addWidget(self.scaley_button)
  182. form2_layout.addRow(self.scalex_label, form2_child_1)
  183. form2_layout.addRow(self.scaley_label, form2_child_2)
  184. form2_layout.addRow(self.scale_link_cb, self.scale_zero_ref_cb)
  185. self.ois_scale = OptionalInputSection(self.scale_link_cb, [self.scaley_entry, self.scaley_button], logic=False)
  186. self.transform_lay.addWidget(self.empty_label3)
  187. ## Offset Title
  188. offset_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.offsetName)
  189. self.transform_lay.addWidget(offset_title_label)
  190. ## Form Layout
  191. form3_layout = QtWidgets.QFormLayout()
  192. self.transform_lay.addLayout(form3_layout)
  193. form3_child_1 = QtWidgets.QHBoxLayout()
  194. form3_child_2 = QtWidgets.QHBoxLayout()
  195. self.offx_label = QtWidgets.QLabel(_("Value X:"))
  196. self.offx_label.setToolTip(
  197. _("Value for Offset action on X axis.")
  198. )
  199. self.offx_label.setFixedWidth(50)
  200. self.offx_entry = FCEntry()
  201. self.offx_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
  202. # self.offx_entry.setFixedWidth(60)
  203. self.offx_button = FCButton()
  204. self.offx_button.set_value(_("Offset X"))
  205. self.offx_button.setToolTip(
  206. _("Offset the selected object(s).\n"
  207. "The point of reference is the middle of\n"
  208. "the bounding box for all selected objects.\n"))
  209. self.offx_button.setFixedWidth(60)
  210. self.offy_label = QtWidgets.QLabel(_("Value Y:"))
  211. self.offy_label.setToolTip(
  212. _("Value for Offset action on Y axis.")
  213. )
  214. self.offy_label.setFixedWidth(50)
  215. self.offy_entry = FCEntry()
  216. self.offy_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
  217. # self.offy_entry.setFixedWidth(60)
  218. self.offy_button = FCButton()
  219. self.offy_button.set_value(_("Offset Y"))
  220. self.offy_button.setToolTip(
  221. _("Offset the selected object(s).\n"
  222. "The point of reference is the middle of\n"
  223. "the bounding box for all selected objects.\n"))
  224. self.offy_button.setFixedWidth(60)
  225. form3_child_1.addWidget(self.offx_entry)
  226. form3_child_1.addWidget(self.offx_button)
  227. form3_child_2.addWidget(self.offy_entry)
  228. form3_child_2.addWidget(self.offy_button)
  229. form3_layout.addRow(self.offx_label, form3_child_1)
  230. form3_layout.addRow(self.offy_label, form3_child_2)
  231. self.transform_lay.addWidget(self.empty_label4)
  232. ## Flip Title
  233. flip_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.flipName)
  234. self.transform_lay.addWidget(flip_title_label)
  235. ## Form Layout
  236. form4_layout = QtWidgets.QFormLayout()
  237. form4_child_hlay = QtWidgets.QHBoxLayout()
  238. self.transform_lay.addLayout(form4_child_hlay)
  239. self.transform_lay.addLayout(form4_layout)
  240. form4_child_1 = QtWidgets.QHBoxLayout()
  241. self.flipx_button = FCButton()
  242. self.flipx_button.set_value(_("Flip on X"))
  243. self.flipx_button.setToolTip(
  244. _("Flip the selected object(s) over the X axis.\n"
  245. "Does not create a new object.\n ")
  246. )
  247. self.flipx_button.setFixedWidth(60)
  248. self.flipy_button = FCButton()
  249. self.flipy_button.set_value(_("Flip on Y"))
  250. self.flipy_button.setToolTip(
  251. _("Flip the selected object(s) over the X axis.\n"
  252. "Does not create a new object.\n ")
  253. )
  254. self.flipy_button.setFixedWidth(60)
  255. self.flip_ref_cb = FCCheckBox()
  256. self.flip_ref_cb.set_value(True)
  257. self.flip_ref_cb.setText(_("Ref Pt"))
  258. self.flip_ref_cb.setToolTip(
  259. _("Flip the selected object(s)\n"
  260. "around the point in Point Entry Field.\n"
  261. "\n"
  262. "The point coordinates can be captured by\n"
  263. "left click on canvas together with pressing\n"
  264. "SHIFT key. \n"
  265. "Then click Add button to insert coordinates.\n"
  266. "Or enter the coords in format (x, y) in the\n"
  267. "Point Entry field and click Flip on X(Y)"))
  268. self.flip_ref_cb.setFixedWidth(50)
  269. self.flip_ref_label = QtWidgets.QLabel(_("Point:"))
  270. self.flip_ref_label.setToolTip(
  271. _("Coordinates in format (x, y) used as reference for mirroring.\n"
  272. "The 'x' in (x, y) will be used when using Flip on X and\n"
  273. "the 'y' in (x, y) will be used when using Flip on Y and")
  274. )
  275. self.flip_ref_label.setFixedWidth(50)
  276. self.flip_ref_entry = EvalEntry2("(0, 0)")
  277. self.flip_ref_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
  278. # self.flip_ref_entry.setFixedWidth(60)
  279. self.flip_ref_button = FCButton()
  280. self.flip_ref_button.set_value(_("Add"))
  281. self.flip_ref_button.setToolTip(
  282. _("The point coordinates can be captured by\n"
  283. "left click on canvas together with pressing\n"
  284. "SHIFT key. Then click Add button to insert."))
  285. self.flip_ref_button.setFixedWidth(60)
  286. form4_child_hlay.addStretch()
  287. form4_child_hlay.addWidget(self.flipx_button)
  288. form4_child_hlay.addWidget(self.flipy_button)
  289. form4_child_1.addWidget(self.flip_ref_entry)
  290. form4_child_1.addWidget(self.flip_ref_button)
  291. form4_layout.addRow(self.flip_ref_cb)
  292. form4_layout.addRow(self.flip_ref_label, form4_child_1)
  293. self.ois_flip = OptionalInputSection(self.flip_ref_cb,
  294. [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=False):
  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. self.app.ui.splitter.setSizes([0, 1])
  324. except AttributeError:
  325. pass
  326. FlatCAMTool.run(self)
  327. self.set_tool_ui()
  328. self.app.ui.notebook.setTabText(2, _("Transform Tool"))
  329. def install(self, icon=None, separator=None, **kwargs):
  330. FlatCAMTool.install(self, icon, separator, shortcut='ALT+R', **kwargs)
  331. def set_tool_ui(self):
  332. ## Initialize form
  333. if self.app.defaults["tools_transform_rotate"]:
  334. self.rotate_entry.set_value(self.app.defaults["tools_transform_rotate"])
  335. else:
  336. self.rotate_entry.set_value(0.0)
  337. if self.app.defaults["tools_transform_skew_x"]:
  338. self.skewx_entry.set_value(self.app.defaults["tools_transform_skew_x"])
  339. else:
  340. self.skewx_entry.set_value(0.0)
  341. if self.app.defaults["tools_transform_skew_y"]:
  342. self.skewy_entry.set_value(self.app.defaults["tools_transform_skew_y"])
  343. else:
  344. self.skewy_entry.set_value(0.0)
  345. if self.app.defaults["tools_transform_scale_x"]:
  346. self.scalex_entry.set_value(self.app.defaults["tools_transform_scale_x"])
  347. else:
  348. self.scalex_entry.set_value(1.0)
  349. if self.app.defaults["tools_transform_scale_y"]:
  350. self.scaley_entry.set_value(self.app.defaults["tools_transform_scale_y"])
  351. else:
  352. self.scaley_entry.set_value(1.0)
  353. if self.app.defaults["tools_transform_scale_link"]:
  354. self.scale_link_cb.set_value(self.app.defaults["tools_transform_scale_link"])
  355. else:
  356. self.scale_link_cb.set_value(True)
  357. if self.app.defaults["tools_transform_scale_reference"]:
  358. self.scale_zero_ref_cb.set_value(self.app.defaults["tools_transform_scale_reference"])
  359. else:
  360. self.scale_zero_ref_cb.set_value(True)
  361. if self.app.defaults["tools_transform_offset_x"]:
  362. self.offx_entry.set_value(self.app.defaults["tools_transform_offset_x"])
  363. else:
  364. self.offx_entry.set_value(0.0)
  365. if self.app.defaults["tools_transform_offset_y"]:
  366. self.offy_entry.set_value(self.app.defaults["tools_transform_offset_y"])
  367. else:
  368. self.offy_entry.set_value(0.0)
  369. if self.app.defaults["tools_transform_mirror_reference"]:
  370. self.flip_ref_cb.set_value(self.app.defaults["tools_transform_mirror_reference"])
  371. else:
  372. self.flip_ref_cb.set_value(False)
  373. if self.app.defaults["tools_transform_mirror_point"]:
  374. self.flip_ref_entry.set_value(self.app.defaults["tools_transform_mirror_point"])
  375. else:
  376. self.flip_ref_entry.set_value((0,0))
  377. def on_rotate(self):
  378. try:
  379. value = float(self.rotate_entry.get_value())
  380. except ValueError:
  381. # try to convert comma to decimal point. if it's still not working error message and return
  382. try:
  383. value = float(self.rotate_entry.get_value().replace(',', '.'))
  384. except ValueError:
  385. self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered for Rotate, "
  386. "use a number."))
  387. return
  388. self.app.worker_task.emit({'fcn': self.on_rotate_action,
  389. 'params': [value]})
  390. # self.on_rotate_action(value)
  391. return
  392. def on_flipx(self):
  393. # self.on_flip("Y")
  394. axis = 'Y'
  395. self.app.worker_task.emit({'fcn': self.on_flip,
  396. 'params': [axis]})
  397. return
  398. def on_flipy(self):
  399. # self.on_flip("X")
  400. axis = 'X'
  401. self.app.worker_task.emit({'fcn': self.on_flip,
  402. 'params': [axis]})
  403. return
  404. def on_flip_add_coords(self):
  405. val = self.app.clipboard.text()
  406. self.flip_ref_entry.set_value(val)
  407. def on_skewx(self):
  408. try:
  409. value = float(self.skewx_entry.get_value())
  410. except ValueError:
  411. # try to convert comma to decimal point. if it's still not working error message and return
  412. try:
  413. value = float(self.skewx_entry.get_value().replace(',', '.'))
  414. except ValueError:
  415. self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered for Skew X, "
  416. "use a number."))
  417. return
  418. # self.on_skew("X", value)
  419. axis = 'X'
  420. self.app.worker_task.emit({'fcn': self.on_skew,
  421. 'params': [axis, value]})
  422. return
  423. def on_skewy(self):
  424. try:
  425. value = float(self.skewy_entry.get_value())
  426. except ValueError:
  427. # try to convert comma to decimal point. if it's still not working error message and return
  428. try:
  429. value = float(self.skewy_entry.get_value().replace(',', '.'))
  430. except ValueError:
  431. self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered for Skew Y, "
  432. "use a number."))
  433. return
  434. # self.on_skew("Y", value)
  435. axis = 'Y'
  436. self.app.worker_task.emit({'fcn': self.on_skew,
  437. 'params': [axis, value]})
  438. return
  439. def on_scalex(self):
  440. try:
  441. xvalue = float(self.scalex_entry.get_value())
  442. except ValueError:
  443. # try to convert comma to decimal point. if it's still not working error message and return
  444. try:
  445. xvalue = float(self.scalex_entry.get_value().replace(',', '.'))
  446. except ValueError:
  447. self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered for Scale X, "
  448. "use a number."))
  449. return
  450. # scaling to zero has no sense so we remove it, because scaling with 1 does nothing
  451. if xvalue == 0:
  452. xvalue = 1
  453. if self.scale_link_cb.get_value():
  454. yvalue = xvalue
  455. else:
  456. yvalue = 1
  457. axis = 'X'
  458. point = (0, 0)
  459. if self.scale_zero_ref_cb.get_value():
  460. self.app.worker_task.emit({'fcn': self.on_scale,
  461. 'params': [axis, xvalue, yvalue, point]})
  462. # self.on_scale("X", xvalue, yvalue, point=(0,0))
  463. else:
  464. # self.on_scale("X", xvalue, yvalue)
  465. self.app.worker_task.emit({'fcn': self.on_scale,
  466. 'params': [axis, xvalue, yvalue]})
  467. return
  468. def on_scaley(self):
  469. xvalue = 1
  470. try:
  471. yvalue = float(self.scaley_entry.get_value())
  472. except ValueError:
  473. # try to convert comma to decimal point. if it's still not working error message and return
  474. try:
  475. yvalue = float(self.scaley_entry.get_value().replace(',', '.'))
  476. except ValueError:
  477. self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered for Scale Y, "
  478. "use a number."))
  479. return
  480. # scaling to zero has no sense so we remove it, because scaling with 1 does nothing
  481. if yvalue == 0:
  482. yvalue = 1
  483. axis = 'Y'
  484. point = (0, 0)
  485. if self.scale_zero_ref_cb.get_value():
  486. self.app.worker_task.emit({'fcn': self.on_scale,
  487. 'params': [axis, xvalue, yvalue, point]})
  488. # self.on_scale("Y", xvalue, yvalue, point=(0,0))
  489. else:
  490. # self.on_scale("Y", xvalue, yvalue)
  491. self.app.worker_task.emit({'fcn': self.on_scale,
  492. 'params': [axis, xvalue, yvalue]})
  493. return
  494. def on_offx(self):
  495. try:
  496. value = float(self.offx_entry.get_value())
  497. except ValueError:
  498. # try to convert comma to decimal point. if it's still not working error message and return
  499. try:
  500. value = float(self.offx_entry.get_value().replace(',', '.'))
  501. except ValueError:
  502. self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered for Offset X, "
  503. "use a number."))
  504. return
  505. # self.on_offset("X", value)
  506. axis = 'X'
  507. self.app.worker_task.emit({'fcn': self.on_offset,
  508. 'params': [axis, value]})
  509. return
  510. def on_offy(self):
  511. try:
  512. value = float(self.offy_entry.get_value())
  513. except ValueError:
  514. # try to convert comma to decimal point. if it's still not working error message and return
  515. try:
  516. value = float(self.offy_entry.get_value().replace(',', '.'))
  517. except ValueError:
  518. self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered for Offset Y, "
  519. "use a number."))
  520. return
  521. # self.on_offset("Y", value)
  522. axis = 'Y'
  523. self.app.worker_task.emit({'fcn': self.on_offset,
  524. 'params': [axis, value]})
  525. return
  526. def on_rotate_action(self, num):
  527. obj_list = self.app.collection.get_selected()
  528. xminlist = []
  529. yminlist = []
  530. xmaxlist = []
  531. ymaxlist = []
  532. if not obj_list:
  533. self.app.inform.emit(_("[WARNING_NOTCL] No object selected. Please Select an object to rotate!"))
  534. return
  535. else:
  536. with self.app.proc_container.new(_("Appying Rotate")):
  537. try:
  538. # first get a bounding box to fit all
  539. for obj in obj_list:
  540. if isinstance(obj, FlatCAMCNCjob):
  541. pass
  542. else:
  543. xmin, ymin, xmax, ymax = obj.bounds()
  544. xminlist.append(xmin)
  545. yminlist.append(ymin)
  546. xmaxlist.append(xmax)
  547. ymaxlist.append(ymax)
  548. # get the minimum x,y and maximum x,y for all objects selected
  549. xminimal = min(xminlist)
  550. yminimal = min(yminlist)
  551. xmaximal = max(xmaxlist)
  552. ymaximal = max(ymaxlist)
  553. self.app.progress.emit(20)
  554. for sel_obj in obj_list:
  555. px = 0.5 * (xminimal + xmaximal)
  556. py = 0.5 * (yminimal + ymaximal)
  557. if isinstance(sel_obj, FlatCAMCNCjob):
  558. self.app.inform.emit(_("CNCJob objects can't be rotated."))
  559. else:
  560. sel_obj.rotate(-num, point=(px, py))
  561. sel_obj.plot()
  562. self.app.object_changed.emit(sel_obj)
  563. # add information to the object that it was changed and how much
  564. sel_obj.options['rotate'] = num
  565. self.app.inform.emit(_('[success]Rotate done ...'))
  566. self.app.progress.emit(100)
  567. except Exception as e:
  568. self.app.inform.emit(_("[ERROR_NOTCL] Due of %s, rotation movement was not executed.") % str(e))
  569. return
  570. def on_flip(self, axis):
  571. obj_list = self.app.collection.get_selected()
  572. xminlist = []
  573. yminlist = []
  574. xmaxlist = []
  575. ymaxlist = []
  576. if not obj_list:
  577. self.app.inform.emit(_("[WARNING_NOTCL] No object selected. Please Select an object to flip!"))
  578. return
  579. else:
  580. with self.app.proc_container.new(_("Applying Flip")):
  581. try:
  582. # get mirroring coords from the point entry
  583. if self.flip_ref_cb.isChecked():
  584. px, py = eval('{}'.format(self.flip_ref_entry.text()))
  585. # get mirroing coords from the center of an all-enclosing bounding box
  586. else:
  587. # first get a bounding box to fit all
  588. for obj in obj_list:
  589. if isinstance(obj, FlatCAMCNCjob):
  590. pass
  591. else:
  592. xmin, ymin, xmax, ymax = obj.bounds()
  593. xminlist.append(xmin)
  594. yminlist.append(ymin)
  595. xmaxlist.append(xmax)
  596. ymaxlist.append(ymax)
  597. # get the minimum x,y and maximum x,y for all objects selected
  598. xminimal = min(xminlist)
  599. yminimal = min(yminlist)
  600. xmaximal = max(xmaxlist)
  601. ymaximal = max(ymaxlist)
  602. px = 0.5 * (xminimal + xmaximal)
  603. py = 0.5 * (yminimal + ymaximal)
  604. self.app.progress.emit(20)
  605. # execute mirroring
  606. for obj in obj_list:
  607. if isinstance(obj, FlatCAMCNCjob):
  608. self.app.inform.emit(_("CNCJob objects can't be mirrored/flipped."))
  609. else:
  610. if axis is 'X':
  611. obj.mirror('X', (px, py))
  612. # add information to the object that it was changed and how much
  613. # the axis is reversed because of the reference
  614. if 'mirror_y' in obj.options:
  615. obj.options['mirror_y'] = not obj.options['mirror_y']
  616. else:
  617. obj.options['mirror_y'] = True
  618. obj.plot()
  619. self.app.inform.emit(_('[success]Flip on the Y axis done ...'))
  620. elif axis is 'Y':
  621. obj.mirror('Y', (px, py))
  622. # add information to the object that it was changed and how much
  623. # the axis is reversed because of the reference
  624. if 'mirror_x' in obj.options:
  625. obj.options['mirror_x'] = not obj.options['mirror_x']
  626. else:
  627. obj.options['mirror_x'] = True
  628. obj.plot()
  629. self.app.inform.emit(_('[success]Flip on the X axis done ...'))
  630. self.app.object_changed.emit(obj)
  631. self.app.progress.emit(100)
  632. except Exception as e:
  633. self.app.inform.emit(_("[ERROR_NOTCL] Due of %s, Flip action was not executed.") % str(e))
  634. return
  635. def on_skew(self, axis, num):
  636. obj_list = self.app.collection.get_selected()
  637. xminlist = []
  638. yminlist = []
  639. if not obj_list:
  640. self.app.inform.emit(_("[WARNING_NOTCL] No object selected. Please Select an object to shear/skew!"))
  641. return
  642. else:
  643. with self.app.proc_container.new(_("Applying Skew")):
  644. try:
  645. # first get a bounding box to fit all
  646. for obj in obj_list:
  647. if isinstance(obj, FlatCAMCNCjob):
  648. pass
  649. else:
  650. xmin, ymin, xmax, ymax = obj.bounds()
  651. xminlist.append(xmin)
  652. yminlist.append(ymin)
  653. # get the minimum x,y and maximum x,y for all objects selected
  654. xminimal = min(xminlist)
  655. yminimal = min(yminlist)
  656. self.app.progress.emit(20)
  657. for obj in obj_list:
  658. if isinstance(obj, FlatCAMCNCjob):
  659. self.app.inform.emit(_("CNCJob objects can't be skewed."))
  660. else:
  661. if axis is 'X':
  662. obj.skew(num, 0, point=(xminimal, yminimal))
  663. # add information to the object that it was changed and how much
  664. obj.options['skew_x'] = num
  665. elif axis is 'Y':
  666. obj.skew(0, num, point=(xminimal, yminimal))
  667. # add information to the object that it was changed and how much
  668. obj.options['skew_y'] = num
  669. obj.plot()
  670. self.app.object_changed.emit(obj)
  671. self.app.inform.emit(_('[success]Skew on the %s axis done ...') % str(axis))
  672. self.app.progress.emit(100)
  673. except Exception as e:
  674. self.app.inform.emit(_("[ERROR_NOTCL] Due of %s, Skew action was not executed.") % str(e))
  675. return
  676. def on_scale(self, axis, xfactor, yfactor, point=None):
  677. obj_list = self.app.collection.get_selected()
  678. xminlist = []
  679. yminlist = []
  680. xmaxlist = []
  681. ymaxlist = []
  682. if not obj_list:
  683. self.app.inform.emit(_("[WARNING_NOTCL] No object selected. Please Select an object to scale!"))
  684. return
  685. else:
  686. with self.app.proc_container.new(_("Applying Scale")):
  687. try:
  688. # first get a bounding box to fit all
  689. for obj in obj_list:
  690. if isinstance(obj, FlatCAMCNCjob):
  691. pass
  692. else:
  693. xmin, ymin, xmax, ymax = obj.bounds()
  694. xminlist.append(xmin)
  695. yminlist.append(ymin)
  696. xmaxlist.append(xmax)
  697. ymaxlist.append(ymax)
  698. # get the minimum x,y and maximum x,y for all objects selected
  699. xminimal = min(xminlist)
  700. yminimal = min(yminlist)
  701. xmaximal = max(xmaxlist)
  702. ymaximal = max(ymaxlist)
  703. self.app.progress.emit(20)
  704. if point is None:
  705. px = 0.5 * (xminimal + xmaximal)
  706. py = 0.5 * (yminimal + ymaximal)
  707. else:
  708. px = 0
  709. py = 0
  710. for obj in obj_list:
  711. if isinstance(obj, FlatCAMCNCjob):
  712. self.app.inform.emit(_("CNCJob objects can't be scaled."))
  713. else:
  714. obj.scale(xfactor, yfactor, point=(px, py))
  715. # add information to the object that it was changed and how much
  716. obj.options['scale_x'] = xfactor
  717. obj.options['scale_y'] = yfactor
  718. obj.plot()
  719. self.app.object_changed.emit(obj)
  720. self.app.inform.emit(_('[success] Scale on the %s axis done ...') % str(axis))
  721. self.app.progress.emit(100)
  722. except Exception as e:
  723. self.app.inform.emit(_("[ERROR_NOTCL] Due of %s, Scale action was not executed.") % str(e))
  724. return
  725. def on_offset(self, axis, num):
  726. obj_list = self.app.collection.get_selected()
  727. xminlist = []
  728. yminlist = []
  729. if not obj_list:
  730. self.app.inform.emit(_("[WARNING_NOTCL] No object selected. Please Select an object to offset!"))
  731. return
  732. else:
  733. with self.app.proc_container.new(_("Applying Offset")):
  734. try:
  735. # first get a bounding box to fit all
  736. for obj in obj_list:
  737. if isinstance(obj, FlatCAMCNCjob):
  738. pass
  739. else:
  740. xmin, ymin, xmax, ymax = obj.bounds()
  741. xminlist.append(xmin)
  742. yminlist.append(ymin)
  743. # get the minimum x,y and maximum x,y for all objects selected
  744. xminimal = min(xminlist)
  745. yminimal = min(yminlist)
  746. self.app.progress.emit(20)
  747. for obj in obj_list:
  748. if isinstance(obj, FlatCAMCNCjob):
  749. self.app.inform.emit(_("CNCJob objects can't be offseted."))
  750. else:
  751. if axis is 'X':
  752. obj.offset((num, 0))
  753. # add information to the object that it was changed and how much
  754. obj.options['offset_x'] = num
  755. elif axis is 'Y':
  756. obj.offset((0, num))
  757. # add information to the object that it was changed and how much
  758. obj.options['offset_y'] = num
  759. obj.plot()
  760. self.app.object_changed.emit(obj)
  761. self.app.inform.emit(_('[success]Offset on the %s axis done ...') % str(axis))
  762. self.app.progress.emit(100)
  763. except Exception as e:
  764. self.app.inform.emit(_("[ERROR_NOTCL] Due of %s, Offset action was not executed.") % str(e))
  765. return
  766. # end of file