ObjectCollection.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870
  1. # ##########################################################
  2. # FlatCAM: 2D Post-processing for Manufacturing #
  3. # http://flatcam.org #
  4. # Author: Juan Pablo Caram (c) #
  5. # Date: 2/5/2014 #
  6. # MIT Licence #
  7. # ##########################################################
  8. # ##########################################################
  9. # File modified by: Dennis Hayrullin #
  10. # File modified by: Marius Stanciu #
  11. # ##########################################################
  12. from PyQt5 import QtGui, QtCore, QtWidgets
  13. from PyQt5.QtCore import Qt, QSettings
  14. from PyQt5.QtGui import QColor
  15. # from PyQt5.QtCore import QModelIndex
  16. from FlatCAMObj import FlatCAMGerber, FlatCAMGeometry, FlatCAMExcellon, FlatCAMCNCjob, FlatCAMDocument, FlatCAMScript
  17. import inspect # TODO: Remove
  18. import FlatCAMApp
  19. import re
  20. import logging
  21. import collections
  22. from copy import deepcopy
  23. from numpy import Inf
  24. import gettext
  25. import FlatCAMTranslation as fcTranslate
  26. import builtins
  27. fcTranslate.apply_language('strings')
  28. if '_' not in builtins.__dict__:
  29. _ = gettext.gettext
  30. log = logging.getLogger('base')
  31. class KeySensitiveListView(QtWidgets.QTreeView):
  32. """
  33. QtGui.QListView extended to emit a signal on key press.
  34. """
  35. def __init__(self, app, parent=None):
  36. super(KeySensitiveListView, self).__init__(parent)
  37. self.setHeaderHidden(True)
  38. self.setEditTriggers(QtWidgets.QTreeView.SelectedClicked)
  39. # self.setRootIsDecorated(False)
  40. # self.setExpandsOnDoubleClick(False)
  41. # Enable dragging and dropping onto the GUI
  42. self.setAcceptDrops(True)
  43. self.filename = ""
  44. self.app = app
  45. keyPressed = QtCore.pyqtSignal(int)
  46. def keyPressEvent(self, event):
  47. # super(KeySensitiveListView, self).keyPressEvent(event)
  48. self.keyPressed.emit(event.key())
  49. def dragEnterEvent(self, event):
  50. if event.mimeData().hasUrls:
  51. event.accept()
  52. else:
  53. event.ignore()
  54. def dragMoveEvent(self, event):
  55. self.setDropIndicatorShown(True)
  56. if event.mimeData().hasUrls:
  57. event.accept()
  58. else:
  59. event.ignore()
  60. def dropEvent(self, event):
  61. drop_indicator = self.dropIndicatorPosition()
  62. m = event.mimeData()
  63. if m.hasUrls:
  64. event.accept()
  65. for url in m.urls():
  66. self.filename = str(url.toLocalFile())
  67. # file drop from outside application
  68. if drop_indicator == QtWidgets.QAbstractItemView.OnItem:
  69. if self.filename == "":
  70. self.app.inform.emit(_("Open cancelled."))
  71. else:
  72. if self.filename.lower().rpartition('.')[-1] in self.app.grb_list:
  73. self.app.worker_task.emit({'fcn': self.app.open_gerber,
  74. 'params': [self.filename]})
  75. else:
  76. event.ignore()
  77. if self.filename.lower().rpartition('.')[-1] in self.app.exc_list:
  78. self.app.worker_task.emit({'fcn': self.app.open_excellon,
  79. 'params': [self.filename]})
  80. else:
  81. event.ignore()
  82. if self.filename.lower().rpartition('.')[-1] in self.app.gcode_list:
  83. self.app.worker_task.emit({'fcn': self.app.open_gcode,
  84. 'params': [self.filename]})
  85. else:
  86. event.ignore()
  87. if self.filename.lower().rpartition('.')[-1] in self.app.svg_list:
  88. object_type = 'geometry'
  89. self.app.worker_task.emit({'fcn': self.app.import_svg,
  90. 'params': [self.filename, object_type, None]})
  91. if self.filename.lower().rpartition('.')[-1] in self.app.dxf_list:
  92. object_type = 'geometry'
  93. self.app.worker_task.emit({'fcn': self.app.import_dxf,
  94. 'params': [self.filename, object_type, None]})
  95. if self.filename.lower().rpartition('.')[-1] in self.app.prj_list:
  96. # self.app.open_project() is not Thread Safe
  97. self.app.open_project(self.filename)
  98. else:
  99. event.ignore()
  100. else:
  101. pass
  102. else:
  103. event.ignore()
  104. class TreeItem(KeySensitiveListView):
  105. """
  106. Item of a tree model
  107. """
  108. def __init__(self, data, icon=None, obj=None, parent_item=None):
  109. super(TreeItem, self).__init__(parent_item)
  110. self.parent_item = parent_item
  111. self.item_data = data # Columns string data
  112. self.icon = icon # Decoration
  113. self.obj = obj # FlatCAMObj
  114. self.child_items = []
  115. if parent_item:
  116. parent_item.append_child(self)
  117. def append_child(self, item):
  118. self.child_items.append(item)
  119. item.set_parent_item(self)
  120. def remove_child(self, item):
  121. child = self.child_items.pop(self.child_items.index(item))
  122. child.obj.clear(True)
  123. child.obj.delete()
  124. del child.obj
  125. del child
  126. def remove_children(self):
  127. for child in self.child_items:
  128. child.obj.clear()
  129. child.obj.delete()
  130. del child.obj
  131. del child
  132. self.child_items = []
  133. def child(self, row):
  134. return self.child_items[row]
  135. def child_count(self):
  136. return len(self.child_items)
  137. def column_count(self):
  138. return len(self.item_data)
  139. def data(self, column):
  140. return self.item_data[column]
  141. def row(self):
  142. return self.parent_item.child_items.index(self)
  143. def set_parent_item(self, parent_item):
  144. self.parent_item = parent_item
  145. def __del__(self):
  146. del self.icon
  147. class ObjectCollection(QtCore.QAbstractItemModel):
  148. """
  149. Object storage and management.
  150. """
  151. groups = [
  152. ("gerber", "Gerber"),
  153. ("excellon", "Excellon"),
  154. ("geometry", "Geometry"),
  155. ("cncjob", "CNC Job"),
  156. ("script", "Scripts"),
  157. ("document", "Document"),
  158. ]
  159. classdict = {
  160. "gerber": FlatCAMGerber,
  161. "excellon": FlatCAMExcellon,
  162. "cncjob": FlatCAMCNCjob,
  163. "geometry": FlatCAMGeometry,
  164. "script": FlatCAMScript,
  165. "document": FlatCAMDocument
  166. }
  167. icon_files = {
  168. "gerber": "share/flatcam_icon16.png",
  169. "excellon": "share/drill16.png",
  170. "cncjob": "share/cnc16.png",
  171. "geometry": "share/geometry16.png",
  172. "script": "share/script_new16.png",
  173. "document": "share/notes16_1.png"
  174. }
  175. # will emit the name of the object that was just selected
  176. item_selected = QtCore.pyqtSignal(str)
  177. root_item = None
  178. # app = None
  179. def __init__(self, app, parent=None):
  180. QtCore.QAbstractItemModel.__init__(self)
  181. self.app = app
  182. # ## Icons for the list view
  183. self.icons = {}
  184. for kind in ObjectCollection.icon_files:
  185. self.icons[kind] = QtGui.QPixmap(
  186. ObjectCollection.icon_files[kind].replace('share', self.app.resource_location))
  187. # Create root tree view item
  188. self.root_item = TreeItem(["root"])
  189. # Create group items
  190. self.group_items = {}
  191. for kind, title in ObjectCollection.groups:
  192. item = TreeItem([title], self.icons[kind])
  193. self.group_items[kind] = item
  194. self.root_item.append_child(item)
  195. # Create test sub-items
  196. # for i in self.root_item.m_child_items:
  197. # print i.data(0)
  198. # i.append_child(TreeItem(["empty"]))
  199. # ## Data # ##
  200. self.checked_indexes = []
  201. # Names of objects that are expected to become available.
  202. # For example, when the creation of a new object will run
  203. # in the background and will complete some time in the
  204. # future. This is a way to reserve the name and to let other
  205. # tasks know that they have to wait until available.
  206. self.promises = set()
  207. # same as above only for objects that are plotted
  208. self.plot_promises = set()
  209. # ## View
  210. self.view = KeySensitiveListView(self.app)
  211. self.view.setModel(self)
  212. self.view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
  213. self.view.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
  214. # self.view.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
  215. # self.view.setDragEnabled(True)
  216. # self.view.setAcceptDrops(True)
  217. # self.view.setDropIndicatorShown(True)
  218. settings = QSettings("Open Source", "FlatCAM")
  219. if settings.contains("notebook_font_size"):
  220. fsize = settings.value('notebook_font_size', type=int)
  221. else:
  222. fsize = 12
  223. font = QtGui.QFont()
  224. font.setPixelSize(fsize)
  225. font.setFamily("Seagoe UI")
  226. self.view.setFont(font)
  227. # ## GUI Events
  228. self.view.selectionModel().selectionChanged.connect(self.on_list_selection_change)
  229. # self.view.activated.connect(self.on_item_activated)
  230. self.view.keyPressed.connect(self.app.ui.keyPressEvent)
  231. # self.view.clicked.connect(self.on_mouse_down)
  232. self.view.customContextMenuRequested.connect(self.on_menu_request)
  233. self.click_modifier = None
  234. def promise(self, obj_name):
  235. FlatCAMApp.App.log.debug("Object %s has been promised." % obj_name)
  236. self.promises.add(obj_name)
  237. def has_promises(self):
  238. return len(self.promises) > 0
  239. def plot_promise(self, plot_obj_name):
  240. self.plot_promises.add(plot_obj_name)
  241. def plot_remove_promise(self, plot_obj_name):
  242. if plot_obj_name in self.plot_promises:
  243. self.plot_promises.remove(plot_obj_name)
  244. def has_plot_promises(self):
  245. return len(self.plot_promises) > 0
  246. def on_mouse_down(self, event):
  247. FlatCAMApp.App.log.debug("Mouse button pressed on list")
  248. def on_menu_request(self, pos):
  249. sel = len(self.view.selectedIndexes()) > 0
  250. self.app.ui.menuprojectenable.setEnabled(sel)
  251. self.app.ui.menuprojectdisable.setEnabled(sel)
  252. self.app.ui.menuprojectcolor.setEnabled(sel)
  253. self.app.ui.menuprojectviewsource.setEnabled(sel)
  254. self.app.ui.menuprojectcopy.setEnabled(sel)
  255. self.app.ui.menuprojectedit.setEnabled(sel)
  256. self.app.ui.menuprojectdelete.setEnabled(sel)
  257. self.app.ui.menuprojectsave.setEnabled(sel)
  258. self.app.ui.menuprojectproperties.setEnabled(sel)
  259. if sel:
  260. self.app.ui.menuprojectgeneratecnc.setVisible(True)
  261. self.app.ui.menuprojectedit.setVisible(True)
  262. self.app.ui.menuprojectsave.setVisible(True)
  263. self.app.ui.menuprojectviewsource.setVisible(True)
  264. self.app.ui.menuprojectcolor.setEnabled(False)
  265. for obj in self.get_selected():
  266. if type(obj) == FlatCAMGerber or type(obj) == FlatCAMExcellon:
  267. self.app.ui.menuprojectcolor.setEnabled(True)
  268. if type(obj) != FlatCAMGeometry:
  269. self.app.ui.menuprojectgeneratecnc.setVisible(False)
  270. if type(obj) != FlatCAMGeometry and type(obj) != FlatCAMExcellon and type(obj) != FlatCAMGerber:
  271. self.app.ui.menuprojectedit.setVisible(False)
  272. if type(obj) != FlatCAMGerber and type(obj) != FlatCAMExcellon and type(obj) != FlatCAMCNCjob:
  273. self.app.ui.menuprojectviewsource.setVisible(False)
  274. if type(obj) != FlatCAMGerber and type(obj) != FlatCAMGeometry and type(obj) != FlatCAMExcellon and \
  275. type(obj) != FlatCAMCNCjob:
  276. # meaning for Scripts and for Document type of FlatCAM object
  277. self.app.ui.menuprojectenable.setVisible(False)
  278. self.app.ui.menuprojectdisable.setVisible(False)
  279. self.app.ui.menuprojectedit.setVisible(False)
  280. self.app.ui.menuprojectproperties.setVisible(False)
  281. self.app.ui.menuprojectgeneratecnc.setVisible(False)
  282. else:
  283. self.app.ui.menuprojectgeneratecnc.setVisible(False)
  284. self.app.ui.menuproject.popup(self.view.mapToGlobal(pos))
  285. def index(self, row, column=0, parent=None, *args, **kwargs):
  286. if not self.hasIndex(row, column, parent):
  287. return QtCore.QModelIndex()
  288. # if not parent.isValid():
  289. # parent_item = self.root_item
  290. # else:
  291. # parent_item = parent.internalPointer()
  292. parent_item = parent.internalPointer() if parent.isValid() else self.root_item
  293. child_item = parent_item.child(row)
  294. if child_item:
  295. return self.createIndex(row, column, child_item)
  296. else:
  297. return QtCore.QModelIndex()
  298. def parent(self, index=None):
  299. if not index.isValid():
  300. return QtCore.QModelIndex()
  301. parent_item = index.internalPointer().parent_item
  302. if parent_item == self.root_item:
  303. return QtCore.QModelIndex()
  304. return self.createIndex(parent_item.row(), 0, parent_item)
  305. def rowCount(self, index=None, *args, **kwargs):
  306. if index.column() > 0:
  307. return 0
  308. if not index.isValid():
  309. parent_item = self.root_item
  310. else:
  311. parent_item = index.internalPointer()
  312. return parent_item.child_count()
  313. def columnCount(self, index=None, *args, **kwargs):
  314. if index.isValid():
  315. return index.internalPointer().column_count()
  316. else:
  317. return self.root_item.column_count()
  318. def data(self, index, role=None):
  319. if not index.isValid():
  320. return None
  321. if role in [Qt.DisplayRole, Qt.EditRole]:
  322. obj = index.internalPointer().obj
  323. if obj:
  324. return obj.options["name"]
  325. else:
  326. return index.internalPointer().data(index.column())
  327. if role == Qt.ForegroundRole:
  328. color = QColor(self.app.defaults['global_proj_item_color'])
  329. color_disabled = QColor(self.app.defaults['global_proj_item_dis_color'])
  330. obj = index.internalPointer().obj
  331. if obj:
  332. return QtGui.QBrush(color) if obj.options["plot"] else QtGui.QBrush(color_disabled)
  333. else:
  334. return index.internalPointer().data(index.column())
  335. elif role == Qt.DecorationRole:
  336. icon = index.internalPointer().icon
  337. if icon:
  338. return icon
  339. else:
  340. return QtGui.QPixmap()
  341. else:
  342. return None
  343. def setData(self, index, data, role=None):
  344. if index.isValid():
  345. obj = index.internalPointer().obj
  346. if obj:
  347. old_name = deepcopy(obj.options['name'])
  348. new_name = str(data)
  349. if old_name != new_name and new_name != '':
  350. # rename the object
  351. obj.options["name"] = deepcopy(data)
  352. self.app.object_status_changed.emit(obj, 'rename', old_name)
  353. # update the SHELL auto-completer model data
  354. try:
  355. self.app.myKeywords.remove(old_name)
  356. self.app.myKeywords.append(new_name)
  357. self.app.shell._edit.set_model_data(self.app.myKeywords)
  358. except Exception as e:
  359. log.debug(
  360. "setData() --> Could not remove the old object name from auto-completer model list. %s" %
  361. str(e))
  362. # obj.build_ui()
  363. self.app.inform.emit(_("Object renamed from <b>{old}</b> to <b>{new}</b>").format(old=old_name,
  364. new=new_name))
  365. return True
  366. def supportedDropActions(self):
  367. return Qt.MoveAction
  368. def flags(self, index):
  369. default_flags = QtCore.QAbstractItemModel.flags(self, index)
  370. if not index.isValid():
  371. return Qt.ItemIsEnabled | default_flags
  372. # Prevent groups from selection
  373. if not index.internalPointer().obj:
  374. return Qt.ItemIsEnabled
  375. else:
  376. return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable | \
  377. Qt.ItemIsDragEnabled | Qt.ItemIsDropEnabled
  378. # return QtWidgets.QAbstractItemModel.flags(self, index)
  379. def append(self, obj, active=False):
  380. FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + " --> OC.append()")
  381. name = obj.options["name"]
  382. # Check promises and clear if exists
  383. if name in self.promises:
  384. self.promises.remove(name)
  385. # FlatCAMApp.App.log.debug("Promised object %s became available." % name)
  386. # FlatCAMApp.App.log.debug("%d promised objects remaining." % len(self.promises))
  387. # Prevent same name
  388. while name in self.get_names():
  389. # ## Create a new name
  390. # Ends with number?
  391. FlatCAMApp.App.log.debug("new_object(): Object name (%s) exists, changing." % name)
  392. match = re.search(r'(.*[^\d])?(\d+)$', name)
  393. if match: # Yes: Increment the number!
  394. base = match.group(1) or ''
  395. num = int(match.group(2))
  396. name = base + str(num + 1)
  397. else: # No: add a number!
  398. name += "_1"
  399. obj.options["name"] = name
  400. obj.set_ui(obj.ui_type(decimals=self.app.decimals))
  401. # Required before appending (Qt MVC)
  402. group = self.group_items[obj.kind]
  403. group_index = self.index(group.row(), 0, QtCore.QModelIndex())
  404. self.beginInsertRows(group_index, group.child_count(), group.child_count())
  405. # Append new item
  406. obj.item = TreeItem(None, self.icons[obj.kind], obj, group)
  407. # Required after appending (Qt MVC)
  408. self.endInsertRows()
  409. # Expand group
  410. if group.child_count() is 1:
  411. self.view.setExpanded(group_index, True)
  412. self.app.should_we_save = True
  413. self.app.object_status_changed.emit(obj, 'append', name)
  414. # decide if to show or hide the Notebook side of the screen
  415. if self.app.defaults["global_project_autohide"] is True:
  416. # always open the notebook on object added to collection
  417. self.app.ui.splitter.setSizes([1, 1])
  418. def get_names(self):
  419. """
  420. Gets a list of the names of all objects in the collection.
  421. :return: List of names.
  422. :rtype: list
  423. """
  424. # FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + " --> OC.get_names()")
  425. return [x.options['name'] for x in self.get_list()]
  426. def get_bounds(self):
  427. """
  428. Finds coordinates bounding all objects in the collection.
  429. :return: [xmin, ymin, xmax, ymax]
  430. :rtype: list
  431. """
  432. FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> OC.get_bounds()")
  433. # TODO: Move the operation out of here.
  434. xmin = Inf
  435. ymin = Inf
  436. xmax = -Inf
  437. ymax = -Inf
  438. # for obj in self.object_list:
  439. for obj in self.get_list():
  440. try:
  441. gxmin, gymin, gxmax, gymax = obj.bounds()
  442. xmin = min([xmin, gxmin])
  443. ymin = min([ymin, gymin])
  444. xmax = max([xmax, gxmax])
  445. ymax = max([ymax, gymax])
  446. except Exception as e:
  447. FlatCAMApp.App.log.warning("DEV WARNING: Tried to get bounds of empty geometry. %s" % str(e))
  448. return [xmin, ymin, xmax, ymax]
  449. def get_by_name(self, name, isCaseSensitive=None):
  450. """
  451. Fetches the FlatCAMObj with the given `name`.
  452. :param name: The name of the object.
  453. :type name: str
  454. :param isCaseSensitive: whether searching of the object is done by name where the name is case sensitive
  455. :return: The requested object or None if no such object.
  456. :rtype: FlatCAMObj or None
  457. """
  458. # FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> OC.get_by_name()")
  459. if isCaseSensitive is None or isCaseSensitive is True:
  460. for obj in self.get_list():
  461. if obj.options['name'] == name:
  462. return obj
  463. else:
  464. for obj in self.get_list():
  465. if obj.options['name'].lower() == name.lower():
  466. return obj
  467. return None
  468. def delete_active(self, select_project=True):
  469. selections = self.view.selectedIndexes()
  470. if len(selections) == 0:
  471. return
  472. active = selections[0].internalPointer()
  473. group = active.parent_item
  474. # send signal with the object that is deleted
  475. # self.app.object_status_changed.emit(active.obj, 'delete')
  476. # some objects add a Tab on creation, close it here
  477. for idx in range(self.app.ui.plot_tab_area.count()):
  478. if self.app.ui.plot_tab_area.widget(idx).objectName() == active.obj.options['name']:
  479. self.app.ui.plot_tab_area.removeTab(idx)
  480. break
  481. # update the SHELL auto-completer model data
  482. name = active.obj.options['name']
  483. try:
  484. self.app.myKeywords.remove(name)
  485. self.app.shell._edit.set_model_data(self.app.myKeywords)
  486. # this is not needed any more because now the code editor is created on demand
  487. # self.app.ui.code_editor.set_model_data(self.app.myKeywords)
  488. except Exception as e:
  489. log.debug(
  490. "delete_active() --> Could not remove the old object name from auto-completer model list. %s" % str(e))
  491. self.app.object_status_changed.emit(active.obj, 'delete', name)
  492. # ############ OBJECT DELETION FROM MODEL STARTS HERE ####################
  493. self.beginRemoveRows(self.index(group.row(), 0, QtCore.QModelIndex()), active.row(), active.row())
  494. group.remove_child(active)
  495. # after deletion of object store the current list of objects into the self.app.all_objects_list
  496. self.app.all_objects_list = self.get_list()
  497. self.endRemoveRows()
  498. # ############ OBJECT DELETION FROM MODEL STOPS HERE ####################
  499. if self.app.is_legacy is False:
  500. self.app.plotcanvas.redraw()
  501. if select_project:
  502. # always go to the Project Tab after object deletion as it may be done with a shortcut key
  503. self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
  504. self.app.should_we_save = True
  505. # decide if to show or hide the Notebook side of the screen
  506. if self.app.defaults["global_project_autohide"] is True:
  507. # hide the notebook if there are no objects in the collection
  508. if not self.get_list():
  509. self.app.ui.splitter.setSizes([0, 1])
  510. def delete_all(self):
  511. FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> OC.delete_all()")
  512. self.app.object_status_changed.emit(None, 'delete_all', '')
  513. try:
  514. self.app.all_objects_list.clear()
  515. self.app.geo_editor.clear()
  516. self.app.exc_editor.clear()
  517. self.app.dblsidedtool.reset_fields()
  518. self.app.panelize_tool.reset_fields()
  519. self.app.cutout_tool.reset_fields()
  520. self.app.film_tool.reset_fields()
  521. self.beginResetModel()
  522. self.checked_indexes = []
  523. for group in self.root_item.child_items:
  524. group.remove_children()
  525. self.endResetModel()
  526. self.app.plotcanvas.redraw()
  527. except Exception as e:
  528. log.debug("ObjectCollection.delete_all() --> %s" % str(e))
  529. def get_active(self):
  530. """
  531. Returns the active object or None
  532. :return: FlatCAMObj or None
  533. """
  534. selections = self.view.selectedIndexes()
  535. if len(selections) == 0:
  536. return None
  537. return selections[0].internalPointer().obj
  538. def get_selected(self):
  539. """
  540. Returns list of objects selected in the view.
  541. :return: List of objects
  542. """
  543. return [sel.internalPointer().obj for sel in self.view.selectedIndexes()]
  544. def get_non_selected(self):
  545. """
  546. Returns list of objects non-selected in the view.
  547. :return: List of objects
  548. """
  549. obj_list = self.get_list()
  550. for sel in self.get_selected():
  551. obj_list.remove(sel)
  552. return obj_list
  553. def set_active(self, name):
  554. """
  555. Selects object by name from the project list. This triggers the
  556. list_selection_changed event and call on_list_selection_changed.
  557. :param name: Name of the FlatCAM Object
  558. :return: None
  559. """
  560. try:
  561. obj = self.get_by_name(name)
  562. item = obj.item
  563. group = self.group_items[obj.kind]
  564. group_index = self.index(group.row(), 0, QtCore.QModelIndex())
  565. item_index = self.index(item.row(), 0, group_index)
  566. self.view.selectionModel().select(item_index, QtCore.QItemSelectionModel.Select)
  567. except Exception as e:
  568. log.error("[ERROR] Cause: %s" % str(e))
  569. raise
  570. def set_all_active(self):
  571. """
  572. Select all objects from the project list. This triggers the
  573. list_selection_changed event and call on_list_selection_changed.
  574. :return: None
  575. """
  576. for name in self.get_names():
  577. self.set_active(name)
  578. def set_exclusive_active(self, name):
  579. """
  580. Make the object with the name in parameters the only selected object
  581. :param name: name of object to be selected and made the only active object
  582. :return: None
  583. """
  584. self.set_all_inactive()
  585. self.set_active(name)
  586. def set_inactive(self, name):
  587. """
  588. Unselect object by name from the project list. This triggers the
  589. list_selection_changed event and call on_list_selection_changed.
  590. :param name: Name of the FlatCAM Object
  591. :return: None
  592. """
  593. # log.debug("ObjectCollection.set_inactive()")
  594. obj = self.get_by_name(name)
  595. item = obj.item
  596. group = self.group_items[obj.kind]
  597. group_index = self.index(group.row(), 0, QtCore.QModelIndex())
  598. item_index = self.index(item.row(), 0, group_index)
  599. self.view.selectionModel().select(item_index, QtCore.QItemSelectionModel.Deselect)
  600. def set_all_inactive(self):
  601. """
  602. Unselect all objects from the project list. This triggers the
  603. list_selection_changed event and call on_list_selection_changed.
  604. :return: None
  605. """
  606. for name in self.get_names():
  607. self.set_inactive(name)
  608. def on_list_selection_change(self, current, previous):
  609. # FlatCAMApp.App.log.debug("on_list_selection_change()")
  610. # FlatCAMApp.App.log.debug("Current: %s, Previous %s" % (str(current), str(previous)))
  611. try:
  612. obj = current.indexes()[0].internalPointer().obj
  613. self.item_selected.emit(obj.options['name'])
  614. if obj.kind == 'gerber':
  615. self.app.inform.emit('[selected]<span style="color:{color};">{name}</span> {tx}'.format(
  616. color='green',
  617. name=str(obj.options['name']),
  618. tx=_("selected"))
  619. )
  620. elif obj.kind == 'excellon':
  621. self.app.inform.emit('[selected]<span style="color:{color};">{name}</span> {tx}'.format(
  622. color='brown',
  623. name=str(obj.options['name']),
  624. tx=_("selected"))
  625. )
  626. elif obj.kind == 'cncjob':
  627. self.app.inform.emit('[selected]<span style="color:{color};">{name}</span> {tx}'.format(
  628. color='blue',
  629. name=str(obj.options['name']),
  630. tx=_("selected"))
  631. )
  632. elif obj.kind == 'geometry':
  633. self.app.inform.emit('[selected]<span style="color:{color};">{name}</span> {tx}'.format(
  634. color='red',
  635. name=str(obj.options['name']),
  636. tx=_("selected"))
  637. )
  638. elif obj.kind == 'script':
  639. self.app.inform.emit('[selected]<span style="color:{color};">{name}</span> {tx}'.format(
  640. color='orange',
  641. name=str(obj.options['name']),
  642. tx=_("selected"))
  643. )
  644. elif obj.kind == 'document':
  645. self.app.inform.emit('[selected]<span style="color:{color};">{name}</span> {tx}'.format(
  646. color='darkCyan',
  647. name=str(obj.options['name']),
  648. tx=_("selected"))
  649. )
  650. except IndexError:
  651. self.item_selected.emit('none')
  652. # FlatCAMApp.App.log.debug("on_list_selection_change(): Index Error (Nothing selected?)")
  653. self.app.inform.emit('')
  654. try:
  655. self.app.ui.selected_scroll_area.takeWidget()
  656. except Exception as e:
  657. FlatCAMApp.App.log.debug("Nothing to remove. %s" % str(e))
  658. self.app.setup_component_editor()
  659. return
  660. if obj:
  661. obj.build_ui()
  662. def on_item_activated(self, index):
  663. """
  664. Double-click or Enter on item.
  665. :param index: Index of the item in the list.
  666. :return: None
  667. """
  668. a_idx = index.internalPointer().obj
  669. if a_idx is None:
  670. return
  671. else:
  672. try:
  673. a_idx.build_ui()
  674. except Exception as e:
  675. self.app.inform.emit('[ERROR] %s: %s' % (_("Cause of error"), str(e)))
  676. raise
  677. def get_list(self):
  678. obj_list = []
  679. for group in self.root_item.child_items:
  680. for item in group.child_items:
  681. obj_list.append(item.obj)
  682. return obj_list
  683. def update_view(self):
  684. self.dataChanged.emit(QtCore.QModelIndex(), QtCore.QModelIndex())