ToolTransform.py 36 KB

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