From 8c7a0d4a8e70ddb722943b709c2d2fc9d0dccc2d Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Thu, 4 Jan 2018 09:26:15 +0100 Subject: [PATCH] CURA-4525 created CuraSceneController and took out logic from ObjectsModel and BuildPlateModel --- cura/BuildPlateModel.py | 52 ++++------------- cura/CuraApplication.py | 27 ++++++--- cura/CuraSceneController.py | 101 ++++++++++++++++++++++++++++++++++ cura/ObjectManager.py | 87 ----------------------------- cura/ObjectsModel.py | 49 +++++++++++++++++ resources/qml/ObjectsList.qml | 16 +++--- 6 files changed, 187 insertions(+), 145 deletions(-) create mode 100644 cura/CuraSceneController.py delete mode 100644 cura/ObjectManager.py create mode 100644 cura/ObjectsModel.py diff --git a/cura/BuildPlateModel.py b/cura/BuildPlateModel.py index 2d558a91b9..73f61202c6 100644 --- a/cura/BuildPlateModel.py +++ b/cura/BuildPlateModel.py @@ -1,7 +1,6 @@ -from UM.Qt.ListModel import ListModel from PyQt5.QtCore import pyqtSignal, pyqtProperty, pyqtSlot -from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator -from UM.Scene.SceneNode import SceneNode + +from UM.Qt.ListModel import ListModel from UM.Scene.Selection import Selection from UM.Logger import Logger from UM.Application import Application @@ -14,7 +13,6 @@ class BuildPlateModel(ListModel): def __init__(self): super().__init__() - Application.getInstance().getController().getScene().sceneChanged.connect(self.updateMaxBuildPlate) # it may be a bit inefficient when changing a lot simultaneously Application.getInstance().getController().getScene().sceneChanged.connect(self._updateSelectedObjectBuildPlateNumbers) Selection.selectionChanged.connect(self._updateSelectedObjectBuildPlateNumbers) @@ -22,50 +20,22 @@ class BuildPlateModel(ListModel): self._active_build_plate = -1 self._selection_build_plates = [] - @pyqtSlot(int) - def setActiveBuildPlate(self, nr): - if nr == self._active_build_plate: - return - Logger.log("d", "Select build plate: %s" % nr) - self._active_build_plate = nr - Selection.clear() - - self.activeBuildPlateChanged.emit() - - @pyqtProperty(int, notify = activeBuildPlateChanged) - def activeBuildPlate(self): - return self._active_build_plate + def setMaxBuildPlate(self, max_build_plate): + self._max_build_plate = max_build_plate + self.maxBuildPlateChanged.emit() ## Return the highest build plate number @pyqtProperty(int, notify = maxBuildPlateChanged) def maxBuildPlate(self): return self._max_build_plate - def updateMaxBuildPlate(self, *args): - if args: - source = args[0] - else: - source = None - if not issubclass(type(source), SceneNode): - return - max_build_plate = self._calcMaxBuildPlate() - changed = False - if max_build_plate != self._max_build_plate: - self._max_build_plate = max_build_plate - changed = True - if changed: - self.maxBuildPlateChanged.emit() - build_plates = [{"name": "Build Plate %d" % (i + 1), "buildPlateNumber": i} for i in range(self._max_build_plate + 1)] - self.setItems(build_plates) - self.itemsChanged.emit() + def setActiveBuildPlate(self, nr): + self._active_build_plate = nr + self.activeBuildPlateChanged.emit() - def _calcMaxBuildPlate(self): - max_build_plate = 0 - for node in DepthFirstIterator(Application.getInstance().getController().getScene().getRoot()): - if node.callDecoration("isSliceable"): - build_plate_number = node.callDecoration("getBuildPlateNumber") - max_build_plate = max(build_plate_number, max_build_plate) - return max_build_plate + @pyqtProperty(int, notify = activeBuildPlateChanged) + def activeBuildPlate(self): + return self._active_build_plate @staticmethod def createBuildPlateModel(): diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 42bd70fdc8..1e10b6a40c 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -33,7 +33,10 @@ from UM.Operations.GroupedOperation import GroupedOperation from UM.Operations.SetTransformOperation import SetTransformOperation from cura.Arranging.Arrange import Arrange +from cura.Arranging.ArrangeObjectsJob import ArrangeObjectsJob +from cura.Arranging.ArrangeObjectsAllBuildPlatesJob import ArrangeObjectsAllBuildPlatesJob from cura.Arranging.ShapeArray import ShapeArray +from cura.MultiplyObjectsJob import MultiplyObjectsJob from cura.Scene.ConvexHullDecorator import ConvexHullDecorator from cura.Operations.SetParentOperation import SetParentOperation from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator @@ -41,9 +44,7 @@ from cura.Scene.BlockSlicingDecorator import BlockSlicingDecorator from cura.Scene.BuildPlateDecorator import BuildPlateDecorator from cura.Scene.CuraSceneNode import CuraSceneNode -from cura.Arranging.ArrangeObjectsJob import ArrangeObjectsJob -from cura.Arranging.ArrangeObjectsAllBuildPlatesJob import ArrangeObjectsAllBuildPlatesJob -from cura.MultiplyObjectsJob import MultiplyObjectsJob +from cura.CuraSceneController import CuraSceneController from UM.Settings.SettingDefinition import SettingDefinition, DefinitionPropertyType from UM.Settings.ContainerRegistry import ContainerRegistry @@ -77,7 +78,7 @@ from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisi from cura.Settings.QualitySettingsModel import QualitySettingsModel from cura.Settings.ContainerManager import ContainerManager -from cura.ObjectManager import ObjectManager +from cura.ObjectsModel import ObjectsModel from cura.BuildPlateModel import BuildPlateModel from PyQt5.QtCore import QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS @@ -211,6 +212,7 @@ class CuraApplication(QtApplication): self._build_plate_model = None self._setting_inheritance_manager = None self._simple_mode_settings_manager = None + self._cura_scene_controller = None self._additional_components = {} # Components to add to certain areas in the interface @@ -398,6 +400,8 @@ class CuraApplication(QtApplication): self._plugin_registry.addSupportedPluginExtension("curaplugin", "Cura Plugin") + self.getCuraSceneController().setActiveBuildPlate(0) # Initialize + def _onEngineCreated(self): self._engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider()) @@ -699,8 +703,9 @@ class CuraApplication(QtApplication): qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 2, "SimpleModeSettingsManager", self.getSimpleModeSettingsManager) - qmlRegisterSingletonType(ObjectManager, "Cura", 1, 2, "ObjectManager", self.getObjectManager) + qmlRegisterSingletonType(ObjectsModel, "Cura", 1, 2, "ObjectsModel", self.getObjectsModel) qmlRegisterSingletonType(BuildPlateModel, "Cura", 1, 2, "BuildPlateModel", self.getBuildPlateModel) + qmlRegisterSingletonType(CuraSceneController, "Cura", 1, 2, "SceneController", self.getCuraSceneController) qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, "MachineActionManager", self.getMachineActionManager) @@ -739,18 +744,22 @@ class CuraApplication(QtApplication): self._material_manager = MaterialManager.createMaterialManager() return self._material_manager - def getObjectManager(self, *args): + def getObjectsModel(self, *args): if self._object_manager is None: - self._object_manager = ObjectManager.createObjectManager() + self._object_manager = ObjectsModel.createObjectsModel() return self._object_manager def getBuildPlateModel(self, *args): if self._build_plate_model is None: self._build_plate_model = BuildPlateModel.createBuildPlateModel() - self._build_plate_model.setActiveBuildPlate(0) # default value return self._build_plate_model + def getCuraSceneController(self, *args): + if self._cura_scene_controller is None: + self._cura_scene_controller = CuraSceneController.createCuraSceneController() + return self._cura_scene_controller + def getSettingInheritanceManager(self, *args): if self._setting_inheritance_manager is None: self._setting_inheritance_manager = SettingInheritanceManager.createSettingInheritanceManager() @@ -1115,7 +1124,7 @@ class CuraApplication(QtApplication): nodes.append(node) job = ArrangeObjectsAllBuildPlatesJob(nodes) job.start() - self.getBuildPlateModel().setActiveBuildPlate(0) + self.getCuraSceneController().setActiveBuildPlate(0) # Initialize # Single build plate @pyqtSlot() diff --git a/cura/CuraSceneController.py b/cura/CuraSceneController.py new file mode 100644 index 0000000000..65723db52c --- /dev/null +++ b/cura/CuraSceneController.py @@ -0,0 +1,101 @@ +from UM.Logger import Logger + +from PyQt5.QtCore import Qt, pyqtSlot, QObject +from PyQt5.QtWidgets import QApplication + +from cura.ObjectsModel import ObjectsModel +from cura.BuildPlateModel import BuildPlateModel + +from UM.Application import Application +from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator +from UM.Scene.SceneNode import SceneNode +from UM.Scene.Selection import Selection + + +class CuraSceneController(QObject): + def __init__(self, objects_model: ObjectsModel, build_plate_model: BuildPlateModel): + super().__init__() + + self._objects_model = objects_model + self._build_plate_model = build_plate_model + self._active_build_plate = -1 + + self._last_selected_index = 0 + self._max_build_plate = 1 # default + + Application.getInstance().getController().getScene().sceneChanged.connect(self.updateMaxBuildPlate) # it may be a bit inefficient when changing a lot simultaneously + + def updateMaxBuildPlate(self, *args): + if args: + source = args[0] + else: + source = None + if not issubclass(type(source), SceneNode): + return + max_build_plate = self._calcMaxBuildPlate() + changed = False + if max_build_plate != self._max_build_plate: + self._max_build_plate = max_build_plate + changed = True + if changed: + self._build_plate_model.setMaxBuildPlate(self._max_build_plate) + build_plates = [{"name": "Build Plate %d" % (i + 1), "buildPlateNumber": i} for i in range(self._max_build_plate + 1)] + self._build_plate_model.setItems(build_plates) + # self.buildPlateItemsChanged.emit() # TODO: necessary after setItems? + + def _calcMaxBuildPlate(self): + max_build_plate = 0 + for node in DepthFirstIterator(Application.getInstance().getController().getScene().getRoot()): + if node.callDecoration("isSliceable"): + build_plate_number = node.callDecoration("getBuildPlateNumber") + max_build_plate = max(build_plate_number, max_build_plate) + return max_build_plate + + ## Either select or deselect an item + @pyqtSlot(int) + def changeSelection(self, index): + modifiers = QApplication.keyboardModifiers() + ctrl_is_active = modifiers & Qt.ControlModifier + shift_is_active = modifiers & Qt.ShiftModifier + + if ctrl_is_active: + item = self._objects_model.getItem(index) + node = item["node"] + if Selection.isSelected(node): + Selection.remove(node) + else: + Selection.add(node) + elif shift_is_active: + polarity = 1 if index + 1 > self._last_selected_index else -1 + for i in range(self._last_selected_index, index + polarity, polarity): + item = self._objects_model.getItem(i) + node = item["node"] + Selection.add(node) + else: + # Single select + item = self._objects_model.getItem(index) + node = item["node"] + Selection.clear() + Selection.add(node) + build_plate_number = node.callDecoration("getBuildPlateNumber") + if build_plate_number is not None and build_plate_number != -1: + self._build_plate_model.setActiveBuildPlate(build_plate_number) + + self._last_selected_index = index + + @pyqtSlot(int) + def setActiveBuildPlate(self, nr): + if nr == self._active_build_plate: + return + Logger.log("d", "Select build plate: %s" % nr) + self._active_build_plate = nr + Selection.clear() + + self._build_plate_model.setActiveBuildPlate(nr) + self._objects_model.setActiveBuildPlate(nr) + + @staticmethod + def createCuraSceneController(): + objects_model = Application.getInstance().getObjectsModel() + build_plate_model = Application.getInstance().getBuildPlateModel() + return CuraSceneController(objects_model = objects_model, build_plate_model = build_plate_model) diff --git a/cura/ObjectManager.py b/cura/ObjectManager.py deleted file mode 100644 index 413f43ed73..0000000000 --- a/cura/ObjectManager.py +++ /dev/null @@ -1,87 +0,0 @@ -from UM.Logger import Logger -from PyQt5.QtCore import pyqtSignal, pyqtProperty, pyqtSlot -from UM.Application import Application -from UM.Qt.ListModel import ListModel -from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator -from UM.Scene.SceneNode import SceneNode -from UM.Scene.Selection import Selection -from PyQt5.QtCore import Qt -from PyQt5.QtWidgets import QApplication -#from cura.Scene.CuraSceneNode import CuraSceneNode -from UM.Preferences import Preferences - - -## Keep track of all objects in the project -class ObjectManager(ListModel): - def __init__(self): - super().__init__() - self._last_selected_index = 0 - self._build_plate_model = Application.getInstance().getBuildPlateModel() - Application.getInstance().getController().getScene().sceneChanged.connect(self._update) - Preferences.getInstance().preferenceChanged.connect(self._update) - self._build_plate_model.activeBuildPlateChanged.connect(self._update) - - def _update(self, *args): - nodes = [] - filter_current_build_plate = Preferences.getInstance().getValue("view/filter_current_build_plate") - active_build_plate_number = self._build_plate_model.activeBuildPlate - for node in DepthFirstIterator(Application.getInstance().getController().getScene().getRoot()): - if not issubclass(type(node), SceneNode) or (not node.getMeshData() and not node.callDecoration("getLayerData")): - continue - if not node.callDecoration("isSliceable"): - continue - node_build_plate_number = node.callDecoration("getBuildPlateNumber") - if filter_current_build_plate and node_build_plate_number != active_build_plate_number: - continue - nodes.append({ - "name": node.getName(), - "isSelected": Selection.isSelected(node), - "isOutsideBuildArea": node.isOutsideBuildArea(), - "buildPlateNumber": node_build_plate_number, - "node": node - }) - nodes = sorted(nodes, key=lambda n: n["name"]) - self.setItems(nodes) - - self.itemsChanged.emit() - - ## Either select or deselect an item - @pyqtSlot(int) - def changeSelection(self, index): - modifiers = QApplication.keyboardModifiers() - ctrl_is_active = modifiers & Qt.ControlModifier - shift_is_active = modifiers & Qt.ShiftModifier - - if ctrl_is_active: - item = self.getItem(index) - node = item["node"] - if Selection.isSelected(node): - Selection.remove(node) - else: - Selection.add(node) - elif shift_is_active: - polarity = 1 if index + 1 > self._last_selected_index else -1 - for i in range(self._last_selected_index, index + polarity, polarity): - item = self.getItem(i) - node = item["node"] - Selection.add(node) - else: - # Single select - item = self.getItem(index) - node = item["node"] - Selection.clear() - Selection.add(node) - build_plate_number = node.callDecoration("getBuildPlateNumber") - if build_plate_number is not None and build_plate_number != -1: - self._build_plate_model.setActiveBuildPlate(build_plate_number) - - self._last_selected_index = index - - # testing - for node in DepthFirstIterator(Application.getInstance().getController().getScene().getRoot()): - if node.callDecoration("getLayerData"): - Logger.log("d", " ##### NODE: %s", node) - - @staticmethod - def createObjectManager(): - return ObjectManager() diff --git a/cura/ObjectsModel.py b/cura/ObjectsModel.py new file mode 100644 index 0000000000..2e83ee9033 --- /dev/null +++ b/cura/ObjectsModel.py @@ -0,0 +1,49 @@ +from UM.Application import Application +from UM.Qt.ListModel import ListModel +from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator +from UM.Scene.SceneNode import SceneNode +from UM.Scene.Selection import Selection +from UM.Preferences import Preferences + + +## Keep track of all objects in the project +class ObjectsModel(ListModel): + def __init__(self): + super().__init__() + + Application.getInstance().getController().getScene().sceneChanged.connect(self._update) + Preferences.getInstance().preferenceChanged.connect(self._update) + + self._build_plate_number = -1 + + def setActiveBuildPlate(self, nr): + self._build_plate_number = nr + self._update() + + def _update(self, *args): + nodes = [] + filter_current_build_plate = Preferences.getInstance().getValue("view/filter_current_build_plate") + active_build_plate_number = self._build_plate_number + for node in DepthFirstIterator(Application.getInstance().getController().getScene().getRoot()): + if not issubclass(type(node), SceneNode) or (not node.getMeshData() and not node.callDecoration("getLayerData")): + continue + if not node.callDecoration("isSliceable"): + continue + node_build_plate_number = node.callDecoration("getBuildPlateNumber") + if filter_current_build_plate and node_build_plate_number != active_build_plate_number: + continue + nodes.append({ + "name": node.getName(), + "isSelected": Selection.isSelected(node), + "isOutsideBuildArea": node.isOutsideBuildArea(), + "buildPlateNumber": node_build_plate_number, + "node": node + }) + nodes = sorted(nodes, key=lambda n: n["name"]) + self.setItems(nodes) + + self.itemsChanged.emit() + + @staticmethod + def createObjectsModel(): + return ObjectsModel() diff --git a/resources/qml/ObjectsList.qml b/resources/qml/ObjectsList.qml index a924959581..04afbe2fb0 100644 --- a/resources/qml/ObjectsList.qml +++ b/resources/qml/ObjectsList.qml @@ -85,7 +85,7 @@ Rectangle anchors.fill: parent; onClicked: { - Cura.BuildPlateModel.setActiveBuildPlate(index); + Cura.SceneController.setActiveBuildPlate(index); } } } @@ -131,7 +131,7 @@ Rectangle Rectangle { height: childrenRect.height - color: Cura.ObjectManager.getItem(index).isSelected ? palette.highlight : index % 2 ? palette.base : palette.alternateBase + color: Cura.ObjectsModel.getItem(index).isSelected ? palette.highlight : index % 2 ? palette.base : palette.alternateBase width: parent.width Label { @@ -139,8 +139,8 @@ Rectangle anchors.left: parent.left anchors.leftMargin: UM.Theme.getSize("default_margin").width width: parent.width - 2 * UM.Theme.getSize("default_margin").width - 30 - text: Cura.ObjectManager.getItem(index) ? Cura.ObjectManager.getItem(index).name : ""; - color: Cura.ObjectManager.getItem(index).isSelected ? palette.highlightedText : (Cura.ObjectManager.getItem(index).isOutsideBuildArea ? palette.mid : palette.text) + text: Cura.ObjectsModel.getItem(index) ? Cura.ObjectsModel.getItem(index).name : ""; + color: Cura.ObjectsModel.getItem(index).isSelected ? palette.highlightedText : (Cura.ObjectsModel.getItem(index).isOutsideBuildArea ? palette.mid : palette.text) elide: Text.ElideRight } @@ -151,8 +151,8 @@ Rectangle anchors.left: nodeNameLabel.right anchors.leftMargin: UM.Theme.getSize("default_margin").width anchors.right: parent.right - text: Cura.ObjectManager.getItem(index).buildPlateNumber != -1 ? Cura.ObjectManager.getItem(index).buildPlateNumber + 1 : ""; - color: Cura.ObjectManager.getItem(index).isSelected ? palette.highlightedText : palette.text + text: Cura.ObjectsModel.getItem(index).buildPlateNumber != -1 ? Cura.ObjectsModel.getItem(index).buildPlateNumber + 1 : ""; + color: Cura.ObjectsModel.getItem(index).isSelected ? palette.highlightedText : palette.text elide: Text.ElideRight } @@ -161,7 +161,7 @@ Rectangle anchors.fill: parent; onClicked: { - Cura.ObjectManager.changeSelection(index); + Cura.SceneController.changeSelection(index); } } } @@ -195,7 +195,7 @@ Rectangle ListView { id: listview - model: Cura.ObjectManager + model: Cura.ObjectsModel width: parent.width delegate: objectDelegate }