ToolTransform.py 34 KB

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