ToolTransform.py 37 KB

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